通用扩展方法解析失败

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)

Sri*_*vel 6

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

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

让我们首先通过删除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)