A group blog from members of the VB team
This is the fourth installment in my series of posts about extension methods. You can find links to the rest of the series here.
Today I'm going to talk about extension methods and late binding. Essentially there isn't much to say about it, other than the fact that we don't support late bound execution of extension methods. For the most part this isn't a big deal as one of the primary benefits of extension methods is its interaction with intellisence which doesn't work in late bound scenarios anyways. Unfortunately, however, there is one big side effect of this decision that you need to be aware of when authoring extension methods. Mainly, we do not allow extension methods to be called off of any expression that is statically typed as "Object". This was necessary to prevent any existing late bound code you may have written from being broken by extension methods. Too see why, consider the following example:
Class C1 Public Sub Method1() Console.WriteLine("Running c1.Method1") End Sub End Class
Class C2 Public Sub Method1() Console.WriteLine("Running c2.Method1") End Sub End Class
Module M1 Public Sub Main() Dim x As Object = Nothing If (SomeCondition()) Then x = New C1 Else x = New C2 End If x.Method1() End SubEnd Module
Here we have a program which uses late binding in order to invoke the method "Method1" on an object that is either of type "c1" or of type "c2". It does this because the static type of the variable "x" is declared as object, which causes the compiler to resolve any calls to "unknown" methods as late bound calls that will be resolved at runtime based on the dynamic type of the object stored in "x". Extension methods, however, are always fully resolved at compile time. As a result, if we were to add an extension method defined on Object, like so:
<Extension()> _ Public Sub Method1(ByVal x As Object) Console.WriteLine("Running Extension method m1.Method1") End Sub
Then the act of simply importing the namespace containing the method would cause the formerly late bound method call to Method1 to be transformed into an early bound call to the extension method. This would not be good, as it would silently change the meaning of the program, which would have the potential of making extension methods very, very dangerous. In fact, we explicitly disallow this in early bound scenarios by always having instance methods defined on a type shadow any extension methods defined on it with the same name. This enables you to import extension methods into your existing code without having to worry about things blowing up in your face.
In the case of late bound calls, however, we can't use the same shadowing semantics that we do with early bound method calls because we don't know the actual type of the object we will be calling the method on, so we don't have a list of methods we can check against at compile time. As a result, the only way we could prevent extension methods from completely breaking existing late bound code was to prevent them from being used on anything typed as object.
This has the effect of slightly changing the meaning of an extension method defined on object. Consider the following non-object extension method:
<Extension()> _ Public Sub Method2(ByVal x As System.Windows.Forms.Control) End Sub
Here the declaration of "Method2" implies that the method is an extension method applicable to the type System.Windows.Froms.Control and any class that derives from it, such as TextBox or DataGridView. In the case of "Method1", however, the method declaration implies that it is applicable to "all types except object". This is a little unfortunate, as it is an inconsistency in the language, but we felt it was much better in this case to be safe than it was to be consistent.
That's all I have for today. In my next post I'll talk about extension methods and generics.
PingBack from http://blogs.msdn.com/vbteam/pages/articles-about-extension-methods.aspx
What about extensions on unconstrained generic type parameters?
I recall Paul Vick's post about resolving extensions on late bound objects and as fantastical as it seemed at the time I can appreciate your decision to drop it. And given the limited interface of Object it would be much effort for little practical benefit to end users.
I suppose the only other compromise would be to disallow extentions on type object with Option Strict Off which is inconsistent and once again kinda useless.
I know I've been kinda emotional lately about the VB9 dream dying (scope reduction) a little in the last few but I think this was a really good call on the part of the VB Team, and wanted to applaud your good judgment, for what my not-always-so-humble opinion's worth.
Thanks for the feedback. We like to get both good feedback and bad feedback about what we are doing, so don't ever feel bad about sharing your opion with us. We wouldn't be able to build great products without it.
Also, the unconstrained type parameter case will behave identically to the object case.
Can someone update the VB9 language overview to reflect all the design and scope decisions since it was last written. I would appreciate an up-to-date and accurate picture of what I'm begging my employer to adopt.
I second Anthony's request for an update to the VB9 document. Not to be negative but its long overdue.
This is the sixth installment in my series of posts about extension methods. You can find links to the
Seems to me that this should have been a compilation error or warning type that could be turned on or off, so that those who do not use late binding can declare extention methods on the object type. In terms of "better safe than sorry", this compilation error could be enabled by default.
Yet another arbitrary restriction imposed by the VB.Net language.
Having just noticed this on an unconstrained (Of T) extension, I'm surprised the error message doesn't at least hint at to the issue. "Option Strict disallows late binding" doesn't point to the problem.
Also there could be an Error Correction Option that just internalises the extension object, i.e. change obj.Ext to Ext(Obj,...
The above is for VS2k8; I haven't checked the behaviour of VS2010.