通用扩展方法解析失败

以下程序无法编译,因为在出​​错的行中,编译器选择使用单个T参数作为分辨率的方法,因为List不适合单个T的通用约束而失败。 编译器无法识别可以使用的其他方法。 如果我删除了单T方法,编译器将正确地找到许多对象的方法。

我已经阅读了两篇关于通用方法解析的博客文章,其中一篇来自JonSkeet,另一篇来自Eric Lippert,但我无法找到解释或解决问题的方法。

显然,使用两个具有不同名称的方法会起作用,但我喜欢这样一个事实:对于这些情况,您只有一种方法。

 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(this T parameter) where T : SomeInterface { } public static void Method(this IEnumerable parameter) where T : SomeInterface { } } class Program { static void Main() { var instance = new SomeImplementation(); var instances = new List(); // works instance.Method(); // Error 1 The type 'System.Collections.Generic.List' // cannot be used as type parameter 'T' in the generic type or method // 'Test.ExtensionMethods.Method(T)'. There is no implicit reference conversion // from 'System.Collections.Generic.List' to 'Test.SomeInterface'. instances.Method(); // works (instances as IEnumerable).Method(); } } } 

方法分辨率表示越接近越好 。 有关确切规则,请参阅博客文章。

什么是更接近的意思? 编译器将查看它是否可以找到完全匹配,如果由于某种原因找不到它将找到下一个可能的兼容方法等等。

让我们首先通过删除SomeInterface约束来编译该方法。

 public static class ExtensionMethods { public static void Method(this T parameter) //where T : SomeInterface { } public static void Method(this IEnumerable parameter) //where T : SomeInterface { } } 

现在编译器很乐意编译,并注意两个方法调用Goes to Method(T)而不是Method(IEnumerable) 。 这是为什么?

因为Method(T)在某种意义上更接近,可以将任何类型作为参数,也不需要任何转换。

为什么Method(IEnumerable)不接近?

这是因为您将变量的编译时类型设置为List ,因此需要从ListIEnumerable的引用转换。 哪个更接近,但根本没有做任何转换。

回到你的问题。

为什么是instances.Method(); 不编译?

再次,如前所述使用Method(IEnumerable)我们需要一些引用转换,所以显然不是更接近。 现在我们只剩下一种方法,它与Method非常接近。 但问题是你用SomeInterface限制了它,并且明确List()不能转换为SomeInterface

问题是(猜测)在编译器选择更近的重载之后检查通用约束。 在这种情况下,这会使选择的最佳过载无效。

您可以通过将变量的静态类型更改为可以工作的IEnumerable来轻松修复它,现在您知道原因了。

 IEnumerable instances = new List(); 

您是否尝试过在没有generics的情况下实现第一个,因为它应该表现相同:

 public static void Method(this SomeInterface parameter) { /*...*/ } 

或者,正如德米特里建议的那样,通过以下方式呼叫第二个:

 instances.Method(); 

但是在这里你需要为每次调用添加

  1. 虽然我知道你不想要它,但我认为你应该重新思考方法名称应该是否相同。 我看不出同一名称如何对一个实例起作用,以及这些实例的集合。 例如,如果您的方法名称是Shoot for T ,那么另一种方法应该听起来像ShootThemAll或类似的东西。

  2. 否则你应该让你的作业略有不同:

     IEnumerable instances = new List(); instances.Method(); //now this should work 
  3. 作为最后一个选项,正如Dimitry在评论中所说,你必须明确指定类型参数。

     instances.Method();