2009-11-04 64 views
0

我对使用所有的IEnumerable<T>扩展方法有点困惑,intellisense总是要求<T>,但我认为有必要一直指定<T>什么时候必须为IEnumerable扩展方法指定类型<T>?

比方说,我有以下几点:

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(); 

我觉得上面的两行代码是sxactly同样的,现在的问题:

如何知道何时指定<T>以及何时跳过?

回答

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是,和xT型的...

在另一方面,这将工作:

Foo((int x) => x); 

,因为现在有足够的类型信息来推断一切。或者你可以做的另一种方式:

Foo<int>(x => x); 

的具体一步一步的规则推论其实相当复杂,你最好离这里阅读的主要来源 - 这是C#语言规范。

1

此功能被称为类型推断。在您的示例中,编译器可以为您自动确定泛型参数类型,因为在调用ConvertAll的方法中,参数lambda返回字符串值(即Name)。所以你甚至可以删除ConvertAll呼叫的<string>部分。与Distict()一样,因为ConvertAll返回List<string>,编译器可以为您声明泛型参数。

至于你的回答,当编译器可以确定类型本身时,泛型参数是多余的和不必要的。大多数情况下,唯一需要传递泛型参数的地方是声明,如List<string> list = new List<string>();。你可以用var代替第一个List<string>,或者当你在lambdas中使用模板作为参数。

+0

它实际上被称为“类型推断”而不是“隐式泛型”。它推断泛型类型*参数*,而不是类型参数。只是要挑剔:) – 2009-11-04 06:24:46

+0

谢谢你的提升。 :)编辑。顺便说一句,如果我非常准确,那么我将拥有111k的声望。但是,唉......我也有时会睡觉,只能用我的双手打字。 :P – Yogesh 2009-11-04 06:28:40

相关问题