views:

119

answers:

4

In a recent question of mine I learned that if there are more than one extension methods with constraints that match the given type, the most specific one will be chosen. This got me thinking - how does the compiler determine which one is "more specific"? And what will the outcome be?

Let's say I have the following classes:

public MyClass : IComparable, IDisposable
{
    // Implementation of members
}

public static class MyExtensions
{
    public static void DoSomething<T>(this T item)
        where T : IComparable
    { /* whatever */ }

    public static void DoSomething<T>(this T item)
        where T : IDisposable
    { /* whatever else */ }
}

If I now use the extension method as

var instance = new MyClass();
instance.DoSomething();

which method will be used? Or will the compiler throw an error?

Note: I'm not saying this is good design, or even that I have a case where I need to do this. But the term "more specific" was loose enough to make me ponder this, and now I have to know! :P

Update: I guess I wasn't really as interested in what will happen in the above example, as in why. It came to my mind since I'd been doing stuff like

public static class CollectionExtensions
{
    public static void DoSomething<T>(this T items) where T : IList { ... }
    public static void DoSomething<T>(this T items) where T : IEnumerable { ... }
}

where the compiler knows to choose the first method for new List<Something>().DoSomething(), since it is "closer" to the type passed. What I was interested in then, was "what does closer in this context mean? How will the compiler react if the constraints are from two different inheritance chains? Why?"

A: 

Generic constraints are not considered as part of method signature. These two methods are considered by compiler as ones with the same signature. So you will get compile error saying that method DoSomething is already defined.

public static void DoSomething<T>(this T item)
    where T : IComparable
{ /* whatever */ }

public static void DoSomething<T>(this T item)
    where T : IDisposable
{ /* whatever else */ }
Andrew Bezzub
+1  A: 

It will not compile at all and throw a compile time error saying call is ambiguish between the two methods.

Type 'MyExtensions' already defines a member called 'DoSomething' with the same parameter types.

EDIT

Here's why compiler gives such error. Extension methods are just syntactic sugars and all they do is bring fluency and readabilty on any type.

Check this code..

var instance = new MyClass();
instance.DoSomething();

Compiler replaces this code as following.

var instance = new MyClass();
MyExtensions.DoSomething(instance);
//Compiler gets confused. Which one to call IComparable or IDisposable

In your case compiler gets confused since there are two matching signatures to the method-call and it gives you the said error.

this. __curious_geek
+6  A: 

The extensions class won't compile, in this case - you can't overload methods based solely on generic constraints.

If you put the two extension methods into different classes, then the calling code wouldn't compile - it would be an ambiguous call, as neither method would be "better" than the other... in both cases the generic type argument would be inferred as MyClass, so there'd just be two conversions from MyClass to MyClass, neither of which is better than the other.

This is basically just a special case of overloading, once you've found out that no instance methods are applicable. I wrote an article on overloading just recently which you may find useful - it doesn't call out this specific case, but it points to the relevant bits of the spec if you want to look in detail.

Jon Skeet
A: 

Consider the following example:

class MyClass {}

static class MyClassExtensions
{
    public static void DoSomething<T>(this T item, List<string> someList)
    {
        Console.WriteLine("Method with List in signature is called.");  
    }

    public static void DoSomething<T>(this T item, IEnumerable<string> someEnumerable)
    {
        Console.WriteLine("Method with IEnumerable in signature is called.");   
    }
}

In this example, when testing with the following:

var myClass = new MyClass();
myClass.DoSomething(new List<string>());

The first method in the extensions class is called. In short, this means that the compiler determines the signature that is nearer the arguments passed, and employs that.

Ioannis Karadimas