通用扩展方法解析失败 [英] Generic extension method resolution fails

查看:115
本文介绍了通用扩展方法解析失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的程序不能编译,因为在错误行中,编译器选择带有单个 T 参数的方法作为分辨率,因为 List< T> 不符合单个 T 的通用约束。编译器不承认有另一种方法可以使用。如果我删除单个 T 方法,编译器将正确地找到许多对象的方法。

我读过两篇关于泛型方法解析的博文,一篇来自JonSkeet 这里和另一个来自Eric Lippert 这里,但我找不到解释或解决问题的方法。



很明显,有两种名称不同的方法可以工作,但我喜欢这种情况下您有一种方法。

  namespace Test 
{
using System.Collections.Generic;

public interface SomeInterface {}

public class SomeImplementation:SomeInterface {}

public static class ExtensionMethods
{
/ /注释掉这一行,以使编译器选择在
下面抛出错误的正确方法public static void Method< T>(this T parameter)其中T:SomeInterface {}

public static void Method< T>(this IEnumerable< T> parameter)其中T:SomeInterface {}
}

class程序
{
static void Main ()
{
var instance = new SomeImplementation();
var instances = new List< SomeImplementation>();

//工程
instance.Method();

//错误1类型'System.Collections.Generic.List< Test.SomeImplementation>'
//不能在泛型类型或方法中用作类型参数'T'$ (T)'。没有从'System.Collections.Generic.List< Test.SomeImplementation>'到'Test.SomeInterface'的隐式引用转换
//。
instances.Method();

//工程
(实例为IEnumerable< SomeImplementation>)。




解决方案

方法解析表示更接近更好。查看博客文章中的确切规则。



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



我们先来制作该方法通过移除 SomeInterface 约束来编译。

  public static class ExtensionMethods 
$ b public static void Method< T>(this T parameter)//其中T:SomeInterface
{}

public static void Method< T>(this IEnumerable< T>参数)//其中T:SomeInterface
{}
}

现在编译器很乐意编译,并且注意这两个方法调用都是 Method(T)而不是方法(IEnumerable< T> code>。为什么会这样?



由于方法(T)更接近于可以将任何类型作为参数的意义并且它也不需要任何转换。

lockquote
为什么方法(IEnumerable< T>)不接近?

这是因为你的变量的编译时类型为 List< ; T> ,所以它需要从 List< T> IEnumerable< T>的引用转换。这是更接近,但远没有做任何转换。



回到您的问题。


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


同样,如前所述,使用方法(IEnumerable< T>)参考转换,所以显然不是更接近。现在我们只剩下一个非常接近的方法是 Method< T> 。但是问题在于你用 SomeInterface 和显然 List< SomeImplementation>()不能转换为 SomeInterface



在编译器选择更紧密的过载之后,(通常猜测)检查通用约束会发生。在这种情况下,这会使所选的最佳重载无效。



您可以通过将变量的静态类型更改为 IEnumerable< SomeImplementation> 这将工作,现在你知道为什么。

  IEnumerable< SomeImplementation> instances = new List< SomeImplementation>(); 


The following program does not compile, because in the line with the error, the compiler chooses the method with a single T parameter as the resolution, which fails because the List<T> does not fit the generic constraints of a single T. The compiler does not recognize that there is another method that could be used. If I remove the single-T method, the compiler will correctly find the method for many objects.

I've read two blog posts about generic method resolution, one from JonSkeet here and another from Eric Lippert here, but I could not find an explanation or a way to solve my problem.

Obviously, having two methods with different names would work, but I like the fact that you have a single method for those cases.

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();
    }
  }
}

解决方案

Method resolution says that closer is better. See the blog post for exact rules.

What does the closer mean? Compiler will see if it can find exact match, if it can't find for some reason it will find next possible compatible methods and so forth.

Let's first make that method compile by removing the SomeInterface constraint.

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 
    { }
}

Now compiler is happy to compile, and do note that both method calls Goes to Method(T) rather than Method(IEnumerable<T>). Why is that?

Because Method(T) is closer in the sense that can take any type as the parameter and also it doesn't require any conversion.

Why is Method(IEnumerable<T>) not closer?

It is because you have the compile time type of the variable as List<T>, so it needs a reference conversion from List<T> to IEnumerable<T>. Which is closer but far from doing no conversions at all.

Back to your question.

Why instances.Method(); doesn't compile?

Again, as said earlier to use Method(IEnumerable<T>) we need some reference conversion, so obviously that's not closer. Now we're left with only one method which is very closer is Method<T>. But the problem is you have constrained it with SomeInterface and clearly List<SomeImplementation>() is not convertible to SomeInterface.

The problem is (am guessing) checking for generic constraints happens after the compiler chooses the closer overload. That invalidates the chosen best overload in this case.

You could easily fix it by changing the static type of the variable to IEnumerable<SomeImplementation> that will work and now you know why.

IEnumerable<SomeImplementation> instances = new List<SomeImplementation>();

这篇关于通用扩展方法解析失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆