澄清C#扩展方法优先级

时间:2015-10-05 11:14:48

标签: c#

我试图通过创建具有相同名称的自己的扩展方法来修改现有扩展方法的行为。我知道只要方法签名不同,这是可能的。我也知道要调用的方法基于 close 签名的方式。

如果我有

public void DoStuff(this List<string> list) {
    //...
}

public void DoStuff<T>(this List<T> list) {
    //...
}

我知道DoStuff会调用new List<string>().DoStuff(),而DoStuff<T>会调用new List<int>().DoStuff()

我不明白的是,当我使用界面时,我没有看到相同的行为。

public interface IBar { }

public class Foo : IBar
{

}

public static class FooHelper
{
    public static void DoStuff(this IBar stuff)
    {
        Console.WriteLine("public static void DoStuff (this IBar stuff)");
    }

    public static void DoStuff<T>(this T stuff)
    {
        Console.WriteLine("public static void DoStuff<T>(this T stuff)");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo();

        foo.DoStuff();
    }
}

在上面的示例中,我期望输出public static void DoStuff (this IBar stuff),但我得到public static void DoStuff<T>(this T stuff)。 我假设接口类型比泛型类型更具体,因此我可以更好地选择DoStuff调用。

为什么通过接口类型签名选择泛型类型签名?

1 个答案:

答案 0 :(得分:3)

Chris的评论是正确的,下面是C#语言规范的更多细节:

调用扩展方法foo.DoStuff()等同于调用静态方法FooHelper.DoStuff(foo)

编译器必须确定要调用的确切方法(第7.6.5.1节方法调用)。首先,它将创建一组候选方法。两个DoStuff方法都在这个集合中,因为

  

o如果F是非泛型的,则F在以下情况下是候选者:

     

•M没有类型参数列表,

     

•F适用于A(§7.5.3.1)。

     

o如果F是通用的且M没有类型参数列表,则F在以下情况下是候选者:

     

•类型推断(第7.5.2节)成功,推断出调用的类型参数列表,并且

     

•一旦推断的类型参数替换相应的方法类型参数,F的参数列表中的所有构造类型都满足其约束(§4.4.4),并且F的参数列表适用于A( §7.5.3.1)。

通用FooHelper.DoStuff方法已经推断了类型参数(第7.5.2节):

  

类型推断作为方法调用(第7.6.5.1节)的绑定时处理的一部分发生,并在调用的重载解析步骤之前发生。

     

如果类型推断成功,则推断的类型参数用于确定后续重载解析的参数类型。

所以候选集是

public static void DoStuff(this IBar stuff)
public static void DoStuff<Foo>(this Foo stuff)

从这个集合中,编译器将选择最佳的调用方法。规则在§7.5.3.2中描述。在这种情况下,将调用泛型方法,因为身份转换(从Foo到Foo)优于从Foo到IBar的转换。