Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are extension methods for interfaces treated as lower priority than less specific ones?

Tags:

c#

I have the following extensions class:

public static class MatcherExtensions
{
    public static ExecMatcher<T1, T2> Match<T1, T2>(this Tuple<T1, T2> item)
    {
        return new ExecMatcher<T1, T2>(item.Item1, item.Item2);
    }

    public static ExecMatcher<T1, T2> Match<T1, T2>(this ITupleMatchable<T1, T2> item)
    {
        var tuple = item.PropertiesToMatch;
        return new ExecMatcher<T1, T2>(tuple.Item1, tuple.Item2);
    }

    public static ExecMatcher<T> Match<T>(this T item) { return new ExecMatcher<T>(item); }
}

If I create a tuple and invoke Match(), it correctly uses the first extension method:

var tuple = Tuple.Create(1, "a");
tuple.Match().With(1, "a")...   // compiles just fine.

If I create a int and invoke Match(), it correctly uses the last extension method:

var one = 1;
one.Match().With(1)... // compiles just fine.

However if I create SomeClass, which implements ITupleMatchable<int, string> and try and match on it, the compiler still chooses the third extension method, rather than the second one, despite the latter being a more specific match:

var obj = new SomeClass(1, "a");
obj.Match().With(1, "a")... // won't compile.

Based on Eric Lippert's answer to a similar question, I have worked around this by putting the third extension method into its own class within a subdirectory, thus creating a longer namespace and so more "distance" between it and the calling code than for the version specific to ITupleMatchable<T1, T2>. This feels like a hack to me though. Is there a neater way of resolving this?

like image 871
David Arno Avatar asked Jul 10 '15 16:07

David Arno


People also ask

What is an advantage of using extension methods?

The only advantage of extension methods is code readability.

Are extension methods good?

And static methods and properties and methods are not thread safe and therefore should be avoided then extension methods and extension properties are bad. We are just tricked to do those because the codes we write will appear as pretty or clean but performance-wise it is not.

Can you override extension methods?

Extension methods cannot be overridden the way classes and instance methods are. They are overridden by a slight trick in how the compiler selects which extension method to use by using "closeness" of the method to the caller via namespaces.

Where are extension methods used?

An extension method must be defined in a top-level static class. An extension method with the same name and signature as an instance method will not be called. Extension methods cannot be used to override existing methods. The concept of extension methods cannot be applied to fields, properties or events.


1 Answers

If you simply cast new SomeClass(1, "a") to ITupleMatchable<int, string>, it will work fine:

var obj = (ITupleMatchable<int, string>)new SomeClass(1, "a");
obj.Match().With(1, "a");

Remember that your obj variable otherwise has a compile-time type of SomeClass. The compiler can "more easily" match the actual class to the third extension method (which is compatible with any type), than it can by having to look at the interface implementations of SomeClass and then matching it to e.g. the second extension method.

But if you provide the this parameter as the actual interface type, then the second extension method is a better fit, because it's exactly the type the method is looking for, rather than being the broader "any type". I.e. it's a more specific declaration, and so is "better".

Note that once the candidate set of extension methods is found (via rules relating to the namespace, etc.), the actual method is determined using normal overload resolution. I.e. having determined that at least one method in your MatcherExtensions class is an eligible extension method, the compiler then goes with the normal overload resolution rules to pick among those. You can find those rules in the C# 5.0 specification, section 7.5.3.

Briefly though: before applying the overload resolution rules (indeed, in order to determine which methods are even eligible), note that the compiler has already decided the generic type parameters. So, as it evaluates the overload resolution, it is looking at Match(SomeClass item) and Match(ITupleMatchable<int, string> item). Hopefully once you consider that, you'll see why, if the variable has the type SomeClass, the compiler picks your third extension preferentially over the second, and vice a versa if the type is ITupleMatchable<int, string>.

like image 195
Peter Duniho Avatar answered Sep 28 '22 00:09

Peter Duniho