我对泛型很满意,但作为一个喜欢了解每个细节的人,我有这个问题。在我的 Where
LINQ 扩展方法的实现中
public static class Extensions
{
public static IEnumerable<T> Where<T>(
this IEnumerable<T> source, Func<T, bool> predicate)
{
foreach (T element in source)
if (predicate(element))
yield return element;
}
}
为什么Where<T>
T
是必要的?为什么不能从IEnumerable<T>
T
推断出类型?换句话说,为什么签名不能
public static IEnumerable<T> Where(this IEnumerable<T> source, Func<T, bool> predicate)
感谢您的任何帮助。
public static IEnumerable<T> Where(this IEnumerable<T> source, Func<T, bool> predicate)
有了这样的签名,T
将是一个未声明的标识符。如果T
未在任何地方定义,则不能只使用IEnumerable<T>
。因此,您需要在方法级别(或类(级别T
泛型参数,以使IEnumerable<T>
有意义。
没有理由认为IEnumerable<T>
和Where<T>
中的T
必须是一回事;您是说它们必须通过将它们都指定为T
IEnumerable<T>
是该方法返回的内容。 Where<T>
用于定义用作参数的泛型类型是什么。可以想象,如果您没有将它们都指定为T
,则这些T
可能是不同的类型。
也就是说:返回类型(IEnumerable<T>
(不一定与参数的类型(Where<T>
中的T
(相关。
参数列表前面的<T>
有点像"变量声明" - 就像你不能只说:
x = 10;
您需要先声明该变量:
int x = 10;
只是在这里它是一个Type
的变量声明。
现在用法,这完全是另一回事 - 编译器会根据用法和现有签名,尝试推断出每个Type
变量中要粘贴哪些"值",但这些签名仍然必须声明。
您正在混合Where<T>
方法的定义和它的用法。
定义(和声明(函数时,需要 type 参数来向编译器显示该方法是泛型的。将类型参数声明为 T
后,可以使用它来显示输入和输出参数的类型(在本例中为 IEnumerable<T>
(。
使用该方法时,您不需要指定它 - 它可以很容易地从第一个输入参数中推断出来。如果您不确定,请尝试以下操作:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var odds = numbers.Where(x => x % 2 != 0);
// test for example odds is IEnumerable<int> if you want confirmation =)
public static class Foo
{
public static T Bar<T>(T input) { return input; }
}
通常会推断出类型。
Foo.Bar("test");
但是在这种情况下会发生什么?
Foo.Bar(null); //error
无法推断类型参数,如果这是在编译时,则会出现编译错误。
Foo.Bar<string>(null); //works