Resolving Extension Methods
Paul Stovell (author of SyncLINQ) has pointed out another issue with Extension Methods. As he writes:
In other words, the compiler favours instance methods over extension methods, but only if it can "see" the instance method at compile time.
The reason is simple: currently, resolving an extension method is just a compiler trick, so compile time is the only time when it CAN be done.
But Paul's comment got me thinking. It occured to me that, if my idea about using interfaces was implemented, then it would be possible to properly solve the problem he described. It would work like this:
First, to recap: I suggest that there should be a mapping between extension methods and interfaces. Think of a set of extension methods as an "extension contract". You can make calls on the methods in that contact either by calling intstance methods (on objects which implement the interface) or by extension methods (for objects which do not implement the interface). This is to solve the versioning problem, but it can also be used to solve the problem Paul described.
How? I'll try to illustrate with an example. Imagine that our "extension contract" is called IExtensionContract. It contains two methods, Foo and Bar. We can call Foo and Bar on objects which don't support IExtensionContract, simply by calling them as extesion methods. But, when we call Foo and Bar on objects which DO support IExtensionContract, we want to call the version that is on the object even if we can't tell that the object supports them at compile time. (That's the heart of the issue, right Paul?)
That could be done if the complier worked like this:
Given this code to compile
ISomething s = ...
s.Foo();
The compiler should compile it as if the programmer had written
This would be possible, if the interface-based versioning solution was adopted, because that solution introduces just enough runtime information about the extension methods - namely the "contract" to which the methods belong. Contrast that to the current situation, where there is no runtime information about extension methods.
Here is the user-authored (i.e. not compiler-generated) code for the example above:
The problem is the methods are *resolved* at compile time.
Suppose you had this:
void DoStuff(this IFoo foo);
class FooFactory
IFoo Create()
If your factory returns an object that provides its own instance implementation of DoStuff, it WILL NOT be invoked - the extension method will instead. This breaks many of the rules around polymorphism and good OO in general...
In other words, the compiler favours instance methods over extension methods, but only if it can "see" the instance method at compile time.
The reason is simple: currently, resolving an extension method is just a compiler trick, so compile time is the only time when it CAN be done.
But Paul's comment got me thinking. It occured to me that, if my idea about using interfaces was implemented, then it would be possible to properly solve the problem he described. It would work like this:
First, to recap: I suggest that there should be a mapping between extension methods and interfaces. Think of a set of extension methods as an "extension contract". You can make calls on the methods in that contact either by calling intstance methods (on objects which implement the interface) or by extension methods (for objects which do not implement the interface). This is to solve the versioning problem, but it can also be used to solve the problem Paul described.
How? I'll try to illustrate with an example. Imagine that our "extension contract" is called IExtensionContract. It contains two methods, Foo and Bar. We can call Foo and Bar on objects which don't support IExtensionContract, simply by calling them as extesion methods. But, when we call Foo and Bar on objects which DO support IExtensionContract, we want to call the version that is on the object even if we can't tell that the object supports them at compile time. (That's the heart of the issue, right Paul?)
That could be done if the complier worked like this:
Given this code to compile
ISomething s = ...
s.Foo();
The compiler should compile it as if the programmer had written
ISomething = ... IExtensionContract c = s as IExtensionContract; if(c == null) StaticExtender.Foo(s); // invoke the extension method else c.Foo(); // invoke the instance method
This would be possible, if the interface-based versioning solution was adopted, because that solution introduces just enough runtime information about the extension methods - namely the "contract" to which the methods belong. Contrast that to the current situation, where there is no runtime information about extension methods.
Here is the user-authored (i.e. not compiler-generated) code for the example above:
public static class StaticExtender : IExtensionContract // "inheriting" from interface here is my suggested addition to the language { public static Foo(this ISomething s){...} public static Bar(this ISomething s){...} } public interface IExtensionContract { void Foo(); void Bar(); }Finally, all the suggested logic I have blogged about previously still applies. The compiler still needs to decide, at compile time, whether the call is to a method in the extension contract or to a like-named (but otherwise unrelated) instance method. All that logic still applies as I described it here.