好吧,您遗漏了使一切正常的最重要部分。类型参数可以通过传入的实际对象参数来推断。
例如:
static class Extensions {
internal static IEnumerable<U> Test<T, U>(
this IEnumerable<T> items,
Func<T, U> converter) {
foreach (T item in items) {
yield return converter(item);
}
}
}
此扩展方法适用于任何IEnumerable 类,并将根据您提供的转换器将枚举中的每个项目转换为另一种类型。这是标准的泛型。
现在,有很多方法可以调用这个方法:
IEnumerable<int> values = Enumerable.Range<int>(1, 10);
Func<int, string> converter = i => i.ToString("0.00");
// Variation 1, explicit calling
IEnumerable<string> results1 = Extensions.Test<int, string>(values, converter);
// Variation 2, explicit calling with type inference
IEnumerable<string> results2 = Extensions.Test(values, converter);
// Variation 3, extension method calling, still providing explicit types
IEnumerable<string> results3 = values.Test<int, string>(converter);
// Variation 4, extension method with type inference
IEnumerable<string> results4 = values.Test(converter);
所有四个变体都调用相同的方法并返回相同的结果。类型推断通过查看传递的参数并根据提供的内容自动推断其类型来工作。在我们上面的示例中,它能够确定 typeT
是 type int
,因为我们将 an 传递IEnumerable<int>
到参数 for IEnumerable<T>
。它还能够推断出 typeU
是 type 的,因为我们传入了一个与 withstring
的初始类型匹配的 Func并返回一个字符串。所以用我们的转换器函数来填充。T
int
Func<T, U>
Func<int, string>
从上面的推论来看,这是一个标准的通用方法。类型推断和扩展方法只不过是方便/语法糖。事实上,如果您反编译输出,您可以看到扩展方法被静态调用替换,并且通常使用显式填写的类型参数来定义。(这取决于您的反编译器和设置的选项)。