我什么时候必须为IEnumerable扩展方法指定类型<t>?</t>

时间:2009-11-04 05:21:51

标签: c#-3.0 types extension-methods ienumerable

我对使用所有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>以及何时跳过它?

2 个答案:

答案 0 :(得分:4)

最简单的方法显然是省略它并查看它是否编译。

实际上,您可以在推断的任何地方省略类型参数;当它们在方法参数的类型中使用时,通常可以推断它们。如果在方法的返回类型中仅使用 ,则无法推断它们。因此,例如,对于Enumerable.Select<T>T将从第一个参数的类型(类型为IEnumerable<T>)推断出来。但是对于Enumerable.Empty<T>(),将不会推断,因为它仅用于方法的返回类型,而不是在任何参数中(因为没有)。

请注意,实际规则比这更复杂,并非所有参数都可推断。假设您有这种方法:

 void Foo<T>(Func<T, T> x);

并尝试使用lambda调用它:

 Foo(x => x);

即使T在这里的参数类型中使用,也无法推断出类型 - 因为lambda中也没有类型规范!就编译器而言,Tx的类型相同,而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中使用模板作为参数时。