Thursday, April 06, 2006

Extension Methods: The Solution

As outlined in my previous post, there are three versioning problems with extension methods:

  1. The behaviour of your program can change unexpectedly
  2. The compiler does not warn you about the changes
  3. Even if the compiler did warn you, your ability to respond is limited
In this post, I’ll outline a suggested solution that fixes all three problems. This will be a lengthy post, so please bear with me (or skip to the conclusion below if you can’t wait ;-)


I suggest that:
Extension methods should belong to interfaces. Think of it as defining an "extension contract", a defined set of methods which can be provided as extension methods.
For example, here’s an extension method called Foo, which belongs to interface IFooable:
public static class Extender: IFooable
public static void Foo(this Thing t)

public interface IFooable
void Foo();
That’s all standard C# 3, except for the bit in bold, which is new. It means, "The static class Extender provides extension methods which implement IFooable".

(The definition of interface IFooable is perfectly ordinary C#, there's nothing new there.)


Every extension method is now associated with a particular “contract for functionality” (the contract represented by the interface). By associating extension methods with defined contracts, we can solve all of the problems noted above.

When the compiler hits this line...

a.Foo() identifies all the applicable methods called Foo [1]. Say it finds an instance method and an extension method, both called Foo.
  • If they both belong to the same interface, the compiler can assume that the instance version is the best choice. The class is, in effect, “overriding” an extension method with a specialized alternative. (In the interests of brevity, I won’t dwell on this point, but please leave a comment below if I haven’t made myself clear.)
  • If the methods do not belong to the same interface, the compiler raises an error [2]. The call is ambiguous because the two methods belong to different contracts for functionality.

Let’s see how this would work in practice. Consider my previous example of an extension method for strings, called Contains. If you wrote that method, you’d have to associate it with an interface. You’d write something like this:
public static class StringExtender: IContainable
public static bool Contains
(this string source,
string value)

public interface IContainable
bool Contains(string value);
Now, imagine that Microsoft release their own Contains method for strings, which is incompatible with yours. The compiler will look at both methods to see which interface they belong to. Microsoft’s Contains may belong to an interface defined by Microsoft[3] or, since its an instance method, it may not belong to any interface at all. In any case, it does not belong to your interface. Therefore, the compiler knows that it’s not a substitute for your Contains.

After detecting that the methods belong to different contracts (interfaces), the compiler raises an error. Fortunately, using interfaces offers us a convenient way to tell the compiler which method we actually want, and thereby resolve the error. Simply include the name of the interface which defines the method you want to call. Or, for instance methods with no interface, use the name of the class. Like this:
string s = "abc";
string t = "a";

//calls your extension method 
//calls Microsoft's instance method 
This hypothetical syntax looks a lot like the existing operator for casting types, except it looks like it's casting methods – which it is, in a way.

To tie all this back to my previous post, the current C# spec allows you to force the call to go to the extension method by writing StringExtender.Contains(s, t).That's like s.(IContainable)Contains(t). The current C# spec does not support the opposite, s.(string)Contains(t), in any way.


There are versioning problems with extension methods. The problems can be solved by making extension methods belong to well-defined contracts for functionally, using standard C# interfaces to define those contracts.

Here is each of the problems I originally stated, and its solution:

Problem: The behaviour of your program can change unexpectedly
Solution: There are no unexpected breakages, because instance methods only replace extension methods which belong to the same contract (interface).

Problem: The compiler does not warn you about the changes
Solution: The compiler detects any ambiguity, and warns you.

Problem: Even if the compiler did warn you, your ability to respond is limited.
Solution: You can force the call in either direction: to the extension method or to the instance.

Stay tuned for my next post, in which I’ll outline some groovy side-effects of this proposal... (and also see my later followup here)


[1] Is there a performance cost, at compile time, in identifying all the possible methods? In the current C# 3 spec, the compiler doesn’t even bother looking for extension methods if an instance one exists. In my suggested approach, it always has to check for extension methods. I would guess that my solution has some compile-time cost, but that the cost would be acceptable on modern hardware.

[2] Or, as I suggested in my previous post, perhaps the compiler could just raise a warning instead, and then go ahead and choose the instance method. That’s not my preferred approach, but it would keep the behaviour in line with Microsoft’s current spec for C# 3 – i.e. the compiler always does what the current spec says, with the difference being it does raise a warning when it knows it's doing something dangerous.

[3] Even if Microsoft gave their interface the same name as yours, the compiler could still tell the difference, just like it does right now for same-named interfaces from different namespaces.


Anonymous Keith J. Farmer said...

Your forget the other workaround, which is due to the fact that extension methods are simply static methods that are marked with an attribute. In case of conflict, instead of making the syntax change to the language you propose, you can always call the static method itself:

FooExtensions.FooMethod(foo, arg1, arg2, arg3)

is the same as

foo.FooMethod(arg1, arg2, arg3)

I agree, the workaround it's not as pretty as the dot-notation, but workarounds aren't known for beauty.

However, your proposed solution brings up an idea: extension interfaces. It'd be interesting seeing what that might look like. Wolf Logan ( has started playing with runtime type generation, which is related.

Fri Apr 07, 11:57:00 AM PDT  
Anonymous Keith J. Farmer said...

I apologize -- if I'd read with more sleep I'd see you did mention it.

Fri Apr 07, 12:27:00 PM PDT  
Blogger John Rusk said...

Thanks for the NeoPrimal link Keith. Yes, "extension interfaces" is an interesting idea. My next post will cover some of those issues, including how how my proposed solution supports a mixin(-ish) concept.

Thanks for mentioning the existing workaround, since perhaps I did not highlight clearly enough why it's not enough. It only works in one direction - i.e. forcing the call to go to the extension. Currently, since you cannot force the call in the other direction (to the instance method) there's no way the compiler can issue errors or warnings. Why? Because if the instance method _is_ the right choice, how would you make the warning go away?

That's what my proposal is really about: creating the language features that would make warnings possible.

(As a spin-off benefit, my proposal allows us to issue no warning at all when the two methods _do_ belong to the same interface - in that case, we can safely pick the instance method as per MS's existing spec.)

Fri Apr 07, 01:04:00 PM PDT  
Anonymous Keith J. Farmer said...

Early on (before joining Microsoft), I commented on a similar problem: how to selectively import extensions? That still doesn't have a solution, though I imagine one will arise eventually.

Thankfully, if it's not addressed in Orcas, I still think it can be a breaking change by extending the import syntax to allow it greater precision.

Of course, this may lead to questions about why extensions are given the least priority in resolution. But then, is it really a good thing to allow extensions to override methods on, perhaps, a security principle?

I personally like the extensions implementation. Yes, it has some problems, but the biggest I see at this point is the lack of conflict warnings. (I also see this as the solution to the checked exceptions argument: while Anders notes versioning problems with checked exceptions, that doesn't mean csc cannot *warn* you about them.) Of course, this is all against a prototype compiler...

Fri Apr 07, 03:21:00 PM PDT  
Blogger John Rusk said...

If I understand you correctly, you're saying that the biggest problem with the current design is the lack of conflict warnings. I absolutely agree. I think that's a problem which must be solved, for the reasons I gave in my previous post.

>how to selectively import extensions?

Pardon my dumb question, but why would you want to do that?

Fri Apr 07, 03:29:00 PM PDT  
Blogger John Rusk said...

PS Jon Skeet mentions "using" enhancements for selective import here. I still don't quite understand why though, given the fact that extensions have the least priority in resolution. Perhaps it would be an issue if several namespaces defined extension methods with the same name...

Fri Apr 07, 03:36:00 PM PDT  
Anonymous Keith J. Farmer said...

Precisely the problem I was thinking about.

FooCorp's extensions conflict with BarInc's. Yet you use each in non-conflicting manners elsewhere in the same codefile. How to do specify which extensions should be applied where, in a way that doesn't always involve doing so at the call site?

Fri Apr 07, 04:04:00 PM PDT  
Blogger John Rusk said...

I guess we're talking about two different problems: conflict between same-named extension methods (as in your comment) and conflict between instance and extension methods (the topic of my post). Of course, it would be nice to find one solution that handled both :-)

>in a way that doesn't always involve doing so at the call site?

How bad would it be if the solution was at the call site? I guess I'm thinking that a solution at the call site beats no solution at all :-)

Fri Apr 07, 04:34:00 PM PDT  
Blogger John Rusk said...

Or perhaps a solution at "using" level which covers 90% of cases and a call-site solution for the rest. I had some ideas along those lines which I've written up in a new post.

BTW I just spotted Jon Skeet's post on extension interfaces, here. That's all I've seen about them on line. Do you know of any other posts on that subject?

Fri Apr 07, 10:56:00 PM PDT  

Links to this post:

Create a Link

<< Home