简体   繁体   中英

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() .

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) . I assumed that the interface type is more specific than a generic type, and therefor a better choice for my DoStuff call.

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:

Calling extension method foo.DoStuff() is equivalent to calling static method FooHelper.DoStuff(foo) .

The compiler has to determine the exact method to invoke (§7.6.5.1 Method invocations). First it will create a set of candidate methods. Both DoStuff methods will be in this set, because

o If F is non-generic, F is a candidate when:

• M has no type argument list, and

• F is applicable with respect to A (§7.5.3.1).

o If F is generic and M has no type argument list, F is a candidate when:

• Type inference (§7.5.2) succeeds, inferring a list of type arguments for the call, and

• 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).

The generic FooHelper.DoStuff method will already have the type parameter inferred (§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.

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. In this case, the generic method will be called, because identity conversion (from Foo to Foo) is better than conversion from Foo to IBar.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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