C# 3.0 何时必须为IEnumerable扩展方法指定类型?

C# 3.0 何时必须为IEnumerable扩展方法指定类型?,c#-3.0,types,extension-methods,ienumerable,C# 3.0,Types,Extension Methods,Ienumerable,我对所有IEnumerable扩展方法的使用有点困惑,intellisense总是要求这样做,但我认为没有必要在任何时候都指定 假设我有以下几点: List<Person> people = GetSomePeople(); 这是怎么回事: List<string> names = people.ConvertAll<string>(p=>p.Name).Distinct<string>().ToList<string>();

我对所有IEnumerable扩展方法的使用有点困惑,intellisense总是要求这样做,但我认为没有必要在任何时候都指定

假设我有以下几点:

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(); 
我认为上面的两行代码实际上是一样的,现在的问题是:


我如何知道何时指定和何时跳过它?

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

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

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

 void Foo<T>(Func<T, T> x);
即使在这里参数的类型中使用了T,也无法推断类型-因为lambda中也没有类型规范!就编译器而言,T是与x相同的类型,x是T类型

另一方面,这将起作用:

 Foo((int x) => x);
因为现在有足够的类型信息来推断一切。或者你可以用另一种方式:

 Foo<int>(x => x);

推理的具体分步规则实际上相当复杂,最好阅读这里的主要源代码,即C语言规范。

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

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

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

 void Foo<T>(Func<T, T> x);
即使在这里参数的类型中使用了T,也无法推断类型-因为lambda中也没有类型规范!就编译器而言,T是与x相同的类型,x是T类型

另一方面,这将起作用:

 Foo((int x) => x);
因为现在有足够的类型信息来推断一切。或者你可以用另一种方式:

 Foo<int>(x => x);

推理的具体分步规则实际上相当复杂,最好阅读这里的主要源代码,即C语言规范。

此功能称为类型推理。在您的示例中,编译器可以自动隐式地为您确定泛型参数类型,因为在对ConvertAll的方法调用中,参数lambda返回一个字符串值,即Name。因此,您甚至可以删除ConvertAll调用的一部分。Distict也是如此,因为ConvertAll返回一个列表,编译器可以为您声明泛型参数


至于您的答案,当编译器可以确定类型本身时,泛型参数是多余的,不必要的。大多数情况下,唯一需要传递泛型参数的地方是声明,如List=newlist;。您可以使用var替换第一个列表,或者在lambdas中将模板用作参数时也可以使用var替换第一个列表。

此功能称为类型推断。在您的示例中,编译器可以自动隐式地为您确定泛型参数类型,因为在对ConvertAll的方法调用中,参数lambda返回一个字符串值,即Name。因此,您甚至可以删除ConvertAll调用的一部分。Distict也是如此,因为ConvertAll返回一个列表,编译器可以为您声明泛型参数


至于您的答案,当编译器可以确定类型本身时,泛型参数是多余的,不必要的。大多数情况下,唯一需要传递泛型参数的地方是声明,如List=newlist;。您可以用var代替第一个列表,或者在lambdas中将模板用作参数时也可以使用var。

它实际上被称为类型推断,而不是隐式泛型。它推断的是泛型类型参数,而不是类型参数。挑剔一点:谢谢你的提醒编辑。顺便说一句,如果我这么准确的话,我会有111k的声誉。但是,唉。。。我有时也睡觉,只能用手打字PIt实际上称为类型推断,而不是隐式泛型。这是推断
泛型类型参数,而不是类型参数。挑剔一点:谢谢你的提醒编辑。顺便说一句,如果我这么准确的话,我会有111k的声誉。但是,唉。。。我有时也睡觉,只能用手打字P