我对使用所有IEnumerable<T>
扩展方法感到有点困惑,intellisense总是要求<T>
,但我认为没有必要指定<T>
at所有时间。
假设我有以下内容:
List<Person> people = GetSomePeople();
这是怎么回事:
List<string> names = people.ConvertAll<string>(p=>p.Name).Distinct<string>().ToList<string>();
与此不同:
List<string> names = people.ConvertAll<string>(p=>p.Name).Distinct().ToList();
我认为上面两行代码完全相同,现在的问题是:
我如何知道何时指定<T>
以及何时跳过它?
答案 0 :(得分:4)
最简单的方法显然是省略它并查看它是否编译。
实际上,您可以在推断的任何地方省略类型参数;当它们在方法参数的类型中使用时,通常可以推断它们。如果在方法的返回类型中仅使用 ,则无法推断它们。因此,例如,对于Enumerable.Select<T>
,T
将从第一个参数的类型(类型为IEnumerable<T>
)推断出来。但是对于Enumerable.Empty<T>()
,将不会推断,因为它仅用于方法的返回类型,而不是在任何参数中(因为没有)。
请注意,实际规则比这更复杂,并非所有参数都可推断。假设您有这种方法:
void Foo<T>(Func<T, T> x);
并尝试使用lambda调用它:
Foo(x => x);
即使T
在这里的参数类型中使用,也无法推断出类型 - 因为lambda中也没有类型规范!就编译器而言,T
与x
的类型相同,而x
的类型为T
...
另一方面,这将起作用:
Foo((int x) => x);
因为现在有足够的类型信息来推断一切。或者你可以用另一种方式做到这一点:
Foo<int>(x => x);
推理的具体分步规则实际上相当复杂,您最好在这里阅读主要来源 - 这是C#语言规范。
答案 1 :(得分:1)
此功能称为类型推断。在您的示例中,编译器可以自动为您隐式确定泛型参数类型,因为在对ConvertAll的方法调用中,参数lambda返回字符串值(即Name)。所以你甚至可以删除ConvertAll调用的<string>
部分。与Distict()相同,因为ConvertAll返回List<string>
,编译器可以为你声明泛型参数。
至于你回答,当编译器可以确定类型本身时,泛型参数是多余的而且是不必要的。大多数情况下,唯一需要传递泛型参数的地方是声明,例如List<string> list = new List<string>();
。您可以使用var代替第一个List<string>
,或者在lambdas中使用模板作为参数时。