简体   繁体   English

阐明C#扩展方法的优先顺序

[英]Clarify C# Extension Method Precedence

I'm trying to modify the behaviour of an existing extension method by creating my own extension method with the same name. 我正在尝试通过创建自己的具有相同名称的扩展方法来修改现有扩展方法的行为。 I know this is possible as long as the method signature is different. 我知道这是可能的,只要方法签名不同即可。 I also know that which method to call is based on how close the signature is. 我也知道调用哪种方法取决于签名的接近程度。

If I have 如果我有

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

and

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

I know that DoStuff will be called for new List<string>().DoStuff() , while DoStuff<T> will be called for new List<int>().DoStuff() . 我知道DoStuff将要求new List<string>().DoStuff()DoStuff<T>将要求new List<int>().DoStuff()

What I don't understand is why I'm not seeing the same behaviour when I'm using an interface. 我不明白的是为什么我在使用界面时看不到相同的行为。

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

In the example above, I expected public static void DoStuff (this IBar stuff) to the output, however I get public static void DoStuff<T>(this T stuff) . 在上面的示例中,我期望将public static void DoStuff (this IBar stuff)输出到输出,但是我得到了public static void DoStuff<T>(this T stuff) I assumed that the interface type is more specific than a generic type, and therefor a better choice for my DoStuff call. 我假设接口类型比泛型类型更具体,因此我的DoStuff调用是更好的选择。

Why does the generic type signature get chosen over the interface type signature? 为什么选择通用类型签名而不是接口类型签名?

Chris' comment is correct, below are some more details from C# Language Specification: 克里斯的评论是正确的,以下是C#语言规范中的更多详细信息:

Calling extension method foo.DoStuff() is equivalent to calling static method FooHelper.DoStuff(foo) . 调用扩展方法foo.DoStuff()等效于调用静态方法FooHelper.DoStuff(foo)

The compiler has to determine the exact method to invoke (§7.6.5.1 Method invocations). 编译器必须确定要调用的确切方法(第7.6.5.1节方法调用)。 First it will create a set of candidate methods. 首先,它将创建一组候选方法。 Both DoStuff methods will be in this set, because 这两个DoStuff方法都将在此集合中,因为

o If F is non-generic, F is a candidate when: o如果F是非泛型的,则在以下情况下F是候选者:

• M has no type argument list, and •M没有类型参数列表,并且

• F is applicable with respect to A (§7.5.3.1). •F适用于A(第7.5.3.1节)。

o If F is generic and M has no type argument list, F is a candidate when: o如果F是泛型且M没有类型参数列表,则在以下情况下F是候选值:

• Type inference (§7.5.2) succeeds, inferring a list of type arguments for the call, and •类型推断(第7.5.2节)成功,为调用推断类型实参列表,并且

• Once the inferred type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of F satisfy their constraints (§4.4.4), and the parameter list of F is applicable with respect to A (§7.5.3.1). •一旦将推断的类型参数替换为相应的方法类型参数,F的参数列表中的所有构造类型都将满足其约束(第4.4.4节),并且F的参数列表适用于A(第7.5节)。 3.1)。

The generic FooHelper.DoStuff method will already have the type parameter inferred (§7.5.2): 通用FooHelper.DoStuff方法将具有推断的类型参数(第7.5.2节):

Type inference occurs as part of the binding-time processing of a method invocation (§7.6.5.1) and takes place before the overload resolution step of the invocation. 类型推断是方法调用(第7.6.5.1节)的绑定时处理的一部分,发生在调用的重载解决步骤之前。

If type inference succeeds, then the inferred type arguments are used to determine the types of arguments for subsequent overload resolution. 如果类型推断成功,则使用推断的类型实参确定用于后续重载解析的参数类型。

So the candidate set is 所以候选集是

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

From this set the compiler will choose the best method to call. 从这个集合中,编译器将选择最佳的调用方法。 The rules are described in §7.5.3.2. 这些规则在第7.5.3.2节中进行了描述。 In this case, the generic method will be called, because identity conversion (from Foo to Foo) is better than conversion from Foo to IBar. 在这种情况下,将调用通用方法,因为身份转换(从Foo到Foo)比从Foo到IBar的转换更好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM