nvo*_*igt 5 c# generics extension-methods overload-resolution
以下程序无法编译,因为在出错的行中,编译器选择使用单个T参数作为分辨率的方法,该方法失败,因为List<T>它不符合单个的通用约束T.编译器无法识别可以使用的其他方法.如果我删除单一T方法,编译器将正确地找到许多对象的方法.
我读过有关泛型方法的高分辨率双向博客文章,一个来自JonSkeet 这里和另一名来自埃里克利珀这里,但我无法找到一个解释或解决我的问题的一种方式.
显然,使用两个具有不同名称的方法会起作用,但我喜欢这样一个事实:对于这些情况,您只有一种方法.
namespace Test
{
using System.Collections.Generic;
public interface SomeInterface { }
public class SomeImplementation : SomeInterface { }
public static class ExtensionMethods
{
// comment out this line, to make the compiler chose the right method on the line that throws an error below
public static void Method<T>(this T parameter) where T : SomeInterface { }
public static void Method<T>(this IEnumerable<T> parameter) where T : SomeInterface { }
}
class Program
{
static void Main()
{
var instance = new SomeImplementation();
var instances = new List<SomeImplementation>();
// works
instance.Method();
// Error 1 The type 'System.Collections.Generic.List<Test.SomeImplementation>'
// cannot be used as type parameter 'T' in the generic type or method
// 'Test.ExtensionMethods.Method<T>(T)'. There is no implicit reference conversion
// from 'System.Collections.Generic.List<Test.SomeImplementation>' to 'Test.SomeInterface'.
instances.Method();
// works
(instances as IEnumerable<SomeImplementation>).Method();
}
}
}
Run Code Online (Sandbox Code Playgroud)
方法分辨率表示越接近越好.有关确切规则,请参阅博客文章.
最近的是什么意思?编译器将查看它是否可以找到完全匹配,如果由于某种原因找不到它将找到下一个可能的兼容方法等等.
让我们首先通过删除SomeInterface约束来编译该方法.
public static class ExtensionMethods
{
public static void Method<T>(this T parameter) //where T : SomeInterface
{ }
public static void Method<T>(this IEnumerable<T> parameter) //where T : SomeInterface
{ }
}
Run Code Online (Sandbox Code Playgroud)
现在编译器很乐意编译,并注意两个方法调用都转到Method(T)而不是Method(IEnumerable<T>).这是为什么?
因为Method(T)在任何类型作为参数的意义上更接近,并且它不需要任何转换.
为什么
Method(IEnumerable<T>)不靠近?
这是因为你有变量的编译时类型List<T>,因此它需要从一个参考转换List<T>到IEnumerable<T>.哪个更接近,但根本没有做任何转换.
回到你的问题.
为什么
instances.Method();不编译?
再说一遍,如前所述,Method(IEnumerable<T>)我们需要一些参考转换,所以显然不会更接近.现在我们只剩下一种非常接近的方法了Method<T>.但问题是你已经限制它SomeInterface并且显然List<SomeImplementation>()不能转换为SomeInterface.
问题是(猜测)在编译器选择更近的重载之后检查通用约束.在这种情况下,这会使选择的最佳过载无效.
您可以通过将变量的静态类型更改为可以轻松修复它IEnumerable<SomeImplementation>,现在您知道原因了.
IEnumerable<SomeImplementation> instances = new List<SomeImplementation>();
Run Code Online (Sandbox Code Playgroud)