I'm a bit confused about the use of all the IEnumerable<T>
extension methods, intellisense is always asking for <T>
, but I don't think it's necessary to specify <T>
at all times.
Let's say I have the following:
List<Person> people = GetSo开发者_JAVA技巧mePeople();
How is this:
List<string> names = people.ConvertAll<string>(p=>p.Name).Distinct<string>().ToList<string>();
different from this:
List<string> names = people.ConvertAll<string>(p=>p.Name).Distinct().ToList();
I think the two lines of code above are sxactly the same, now the question:
How do I know when to specify <T>
and when to skip it?
The simplest way is obviously to omit it and see if it compiles.
In practice, you can omit type parameters wherever they are inferred; and they can normally be inferred when they are used in the type of a method parameter than you specify. They cannot be inferred if they're used only in the return type of the method. Thus, for example, for Enumerable.Select<T>
, T
will be inferred from the type of first argument (which is of type IEnumerable<T>
). But for Enumerable.Empty<T>()
, will not be inferred, because it's only used in return type of the method, and not in any arguments (as there are none).
Note that the actual rules are more complex than that, and not all arguments are inferable. Say you have this method:
void Foo<T>(Func<T, T> x);
and you try to call it with a lambda:
Foo(x => x);
Even though T
is used in type of argument here, there's no way to infer the type - since there are no type specifications in the lambda either! As far as compiler is concerned, T
is the same type x
is, and x
is of type T
...
On the other hand, this will work:
Foo((int x) => x);
since now there is sufficient type information to infer everything. Or you could do it the other way:
Foo<int>(x => x);
The specific step-by-step rules for inference are in fact fairly complicated, and you'd be best off reading the primary source here - which is C# language specification.
This feature is known as type inference. In your example, the compiler can automatically determine the generic argument type implicitly for you because in the method call to ConvertAll, the parameter lambda returns a string value (i.e. Name). So you can even remove the <string>
part of ConvertAll call. The same is with Distict(), as ConvertAll returns a List<string>
and the compiler can declare the generic argument for you.
As for you answer, when the compiler can determine the type itself, the generic argument is redundant and unnecessary. Most of the times, the only place where you need to pass the generic argument is the declaration, like, List<string> list = new List<string>();
. You can substitute the first List<string>
with var instead or when you are using templates as parameters in lambdas too.
精彩评论