澄清C#扩展方法优先级

澄清C#扩展方法优先级,c#,C#,我试图通过创建自己的同名扩展方法来修改现有扩展方法的行为。我知道这是可能的,只要方法签名不同。我还知道调用哪个方法取决于签名的接近程度 如果我有 public void DoStuff(this List<string> list) { //... } 在上面的例子中,我期望输出publicstaticvoiddostuff(这个IBar东西),但是我得到publicstaticvoiddostuff(这个T东西)。 我假设接口类型比泛型类型更具体,因此对于我的DoStuf

我试图通过创建自己的同名扩展方法来修改现有扩展方法的行为。我知道这是可能的,只要方法签名不同。我还知道调用哪个方法取决于签名的接近程度

如果我有

public void DoStuff(this List<string> list) {
    //...
}
在上面的例子中,我期望输出
publicstaticvoiddostuff(这个IBar东西)
,但是我得到
publicstaticvoiddostuff(这个T东西)
。 我假设接口类型比泛型类型更具体,因此对于我的
DoStuff
调用来说是更好的选择


为什么选择泛型类型签名而不是接口类型签名?

Chris的评论是正确的,下面是C语言规范中的一些详细信息:

调用扩展方法
foo.DoStuff()
相当于调用静态方法
fooheloper.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)
publicstaticvoiddostuff(这是IBar的东西)
公共静态void DoStuff(这个Foo东西)

编译器将从该集合中选择要调用的最佳方法。规则见§7.5.3.2。在这种情况下,将调用泛型方法,因为标识转换(从Foo到Foo)比从Foo到IBar的转换要好。

我不确定规范到底说了什么,但我会假设它是因为泛型方法可以精确匹配(T=Foo是精确匹配),而IBar需要一些推断。如果您实际将foo声明为IBar(即
ibarfoo=newfoo();
),那么它将执行您想要的操作。我想你只需要阅读c#规范中关于重载的规则,就可以确切地了解为什么它会选择一个而不是另一个。如果你想知道为什么要选择这个设计,那是另一回事。。。最后,我想Eric Lippert已经写了一些关于重载解析的文章,可能会涉及到这一点。另外,这与扩展方法无关。如果你把它们称为普通方法,你也会得到同样的行为。谢谢你的评论。我的想法也是这样的。我还发现了Eric Lippert的文章,它们非常有用。Eric的所有文章都非常有用。可悲的是,我认为他发表的文章比他在微软的时候要少,但花时间浏览他所有的文章,阅读任何你觉得有点意思的东西都是值得的。谢谢。我对泛型类型如何处理的理解是我的思维过程出错的地方。这个解释对我有帮助。好东西。我试图在规范中找到一些东西,但我认为我使用的规范已经过时了(谷歌在顶部出现了VS2003,所以我怀疑是早期版本,可能是泛型之前的版本)。谢谢你充实了我的思想。:)@克里斯,不客气。C语言规范随Visual Studio一起安装在C:\Program Files(x86)\Microsoft Visual Studio 14.0\VC\Specifications\1033中
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<Foo>(this Foo stuff)