Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic extension methods in C#: what will happen in this edge case?

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?"

like image 618
Tomas Aschan Avatar asked Jul 26 '10 06:07

Tomas Aschan


2 Answers

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.

like image 83
Jon Skeet Avatar answered Oct 28 '22 08:10

Jon Skeet


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.

like image 26
this. __curious_geek Avatar answered Oct 28 '22 08:10

this. __curious_geek