好的,您漏掉了最重要的部分,这使得所有内容都可行。类型参数可以由传入的实际对象参数推断出来。
例如:
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");
IEnumerable<string> results1 = Extensions.Test<int, string>(values, converter);
IEnumerable<string> results2 = Extensions.Test(values, converter);
IEnumerable<string> results3 = values.Test<int, string>(converter);
IEnumerable<string> results4 = values.Test(converter);
所有四种变体都调用相同的方法并返回相同的结果。类型推断通过查看传递的参数并根据所提供的内容自动推断它们的类型来工作。在我们上面的示例中,它能够确定类型
T
是
int
类型,因为我们将
IEnumerable<int>
传递到了
IEnumerable<T>
参数中。它还能够推断出类型
U
是
string
类型,因为我们传递了一个与
T
的初始类型匹配的Func并返回一个字符串。因此,
Func<T, U>
被填充了我们的转换器函数
Func<int, string>
。
从上述推断可以看出,这是一个标准的通用方法。类型推断和扩展方法只不过是方便/语法糖。实际上,如果你反编译输出,你会看到扩展方法被替换为静态调用,并且通常定义了明确填写的类型参数。(这取决于你的反编译器和设置选项)。
int
。使用泛型,它不仅适用于您的一个案例,而且适用于任何类似的情况,但具有不同的类型。这将对未来找到您问题的访问者更有帮助。 - Servy