Extension Methods (part 2)

Extension Methods (part 2)

  • Comments 10

In my previous post I gave a high level overview of some of the benefits of using Extension Methods in VB 9.0. Today I'm going to delve into some of the details about how to define extension methods and then use them in your programs.

Defining Extension Methods


You can define an extension method by creating a method in a VB module and decorating it with the System.Runtime.CompilerServices.Extension attribute. The type of the methods first parameter specifies the type the method extends, and the remaining arguments indicate the method's signature. In general, we allow an extension method to be defined on any type that can be represented in a VB signature, with the exception of param arrays, optional arguments, and generic type parameters to (because modules may not be generic). Specifically, this enables you to define extension methods on any of the following types:

  1. Classes (Reference Types)
  2. Structures (Value Types)
  3. Interfaces
  4. Delegates
  5. ByRef / ByVal arguments
  6. Generic method parameters
  7. Arrays

Consuming Extension Methods

In order to consume an extension method, you simply need to bring it into scope. The method will then be visible in intellisence and callable as if it was an instance method. This is a rather simple statement, but it has several interesting implications. In particular, this is a distinct departure from the behavior we implemented in the May 2006 Linq CTP, where we required each type that defined extension methods to be individually imported before its extensions could be used. Although this had some advantages, such as providing fine grained control over extension method visibility, it also suffered from numerous drawbacks.

One big problem was the fact that it was different than C#, which makes extension methods visible by importing the namespace that contains the types that define them. To see why this is an issue, consider the following 2 example C# class libraries:

Example 1:


namespace CSharpExtensionMethodLibrary
{
    static class Extensions1
    {
        public static void ExtensionMethod1(this string x)
        {
        }
    }

    static
class Extensions2
    {
        public static void ExtensionMethod2(this string x)
        {
        }
    }

    static class Extensions3
    {
        public static void ExtensionMethod3(this string x)
        {
        }
    }

    static class Extensions4
    {
        public static void ExtensionMethod4(this string x)
        {
        }
    }

    static class Extensions5
    {
        public static void ExtensionMethod5(this string x)
        {
        }
    }
};

Example 2:


namespace
CSharpExtensionMethodLibrary
{
    static class Extensions
    {
        public static void ExtensionMethod1(this string x)
        {
        }

        public static void ExtensionMethod2(this string x)
        {
        }

        public static void ExtensionMethod3(this string x)
        {
        }

        public static void ExtensionMethod4(this string x)
        {
        }

        public static void ExtensionMethod5(this string x)
        {
        }
    }
};

Here the first example defines an extension method library with the extensions defined in several different static classes all in the same namespace. The second example defines the same extension methods concentrated into a single class. From the point of view of a C# consumer, these libraries are equivalent. To consume them, a C# program simply needs to import their containing namespace. For a VB consumer in the May 2006 Linq CTP, however, these libraries are very different. To consume the first one, a VB programmer would need to add 5 imports statements to his code, where as with the second one he would only need to add one.

Using Example 2 from C#:

using CSharpExtensionMethodLibrary;

class C1
{
   
void Main()
   

       
string x = "";
       
x.Extension1();
       
x.Extension2();
       
x.Extension3();
       
x.Extension4();
       
x.Extension5();
   
}
}

Using Example 2 from VB in the May 2006 CTP:

Imports CSharpExtensionMethodLibrary.Extension1
Imports CSharpExtensionMethodLibrary.Extension2
Imports CSharpExtensionMethodLibrary.Extension3
Imports CSharpExtensionMethodLibrary.Extension4
Imports CSharpExtensionMethodLibrary.Extension5

Module M1
   
Sub Main()
       
Dim x as String = ""
       
x.Extension1()
       
x.Extension2()
       
x.Extension3()
       
x.Extension4()
   
End Sub
End Module

Obviously this has the potential of causing API usability problems for VB programmers consuming C# extension methods.


However, by restricting our rules to only allow extension methods to be defined in modules and changing the rules for extension method visibility to include "any extension method in scope", we were able to solve this problem and achieve parity with C# in a way that meshed well with existing VB language concepts. This works because VB modules have the property of "hoisting" there members into their containing namespace, so importing a namespace will also bring the extension methods defined in its types into scope. Therefore, by treating C# static classes as VB modules (for the purposes of extension methods), we can ensure that VB customers have an API usability experience that is comparable to C#.

Using Example2.dll from VB in Orcas:

Imports CSharpExtensionMethodLibrary

Module M1
   
Sub Main()
       
Dim x = ""
       
x.Extension1()
       
x.Extension2()
       
x.Extension3()
       
x.Extension4()
   
End Sub
End Module

However, we still retain the ability to import a module directly, so the fine grained control offered by the May 2006 CTP is available for anyone who needs it. We've also introduced shadowing rules that give you some degree of control in resolving conflicts between extension methods. More explicitly, we break extension methods into a series of precedence levels based on the mechanism used to bring them into scope. In the event of a conflict between two methods with identical signatures and different precedence levels, we will pick the one with the higher precedence level. The complete list of these levels is shown below (items with lower numbers have higher precedence).

  1. Extension methods defined inside the current module (if there is one)
  2. Extension methods defined inside types in the current namespace or any of its parents, with child namespaces having higher precedence than parent namespaces
  3. Extension methods defined inside any type imports in the current file
  4. Extension methods defined inside any namespace imports in the current file
  5. Extension methods defined inside any project-level type imports
  6. Extension methods defined inside any project-level namespace imports

In the event that these shadowing rules are not enough to resolve ambiguities you may always bind to a particular method by using the old Whidbey-Style shared method syntax (i.e. StringExtensions.Speak(x) as opposed to x.Speak()).

I think that is enough for today. Fortunately, I've only scratched the surface. In my next several posts I will dig into some of the issues you need to be aware of when writing extension method libraries, including:

  1. Extension methods on Value Types
  2. VB Late Binding and Object Extensions
  3. Type Inference rules for Generic Extension Methods
  4. Extension method Versioning Issues
Leave a Comment
  • Please add 2 and 3 and type the answer here:
  • Post
  • Will we have the ability to add an Interface to a type?

    Would we be able to add a virtual Interface to a type for use with Generics?

    One of the problems with Generics is that existing Interfaces don't tell you enough about the types capabilities.  For example, it would be nice to be able to distinguish between Numeric and Non-numeric or Integer and Floating Point types.

  • Hi Keith,

    No, you will not be able to add an Interface to a type without directly modifying its definition. We did have some plans to implement "Dynamic Interfaces" in order to enable type safe duck typing, which would enable the scenarios you descrube. but this feature was cut from Orcas because of scheduling constraints. However, I will forward your request over to the language design team and let them know that you are interested in this feature so that they can consider it for inclusion in a later version of the product.

    -Scott

  • Those scheduling constraints that seem to continually screw over the language until the next version are starting to become bothersome.

    If the CLR isn't changing then VB could theoretically have shorter development cycles with smaller features sets all emitting backwards compatable IL. At least then "until a future version" wouldn't feel like "We're going to leave you disappointed and bitter for another two years". VB 2003 to 2005 and now 2005 to 2007.

    That all said this looks good and I'm happy to see the flexibility you have with this as far as ByVal and ByRef, etc. Thanks for saving the occassional useful feature for this version.

    I disagree with the Modules only rule for one reason only - that I disagree with the auto-hoisting requirement on Modules. I love the auto-shared quality of modules and don't mind that I can't nest them, though often I'd like to. What I would like though is to turn off the auto-static import of a module (with an attribute or something) otherwise I have to make all my utility classes non-module classes so that their members don't clutter my intellisense. I'm sure you might get to this issue 4 versions from now but I thought I'd throw my name into the feature hat now.

    Still, great job- - I look forward to this next version daily (though I'll miss Dynamic Interfaces soo much).

    -Anthony D. Green, MCPD

    P.S. "One big problem was the fact that it was different than C#" is the worst possible way to start out a justification for a VB language design decision. I can infer that you meant it different but still it stings a little to hear it.

  • Here is a list of links to posts on this blog that talk about extension methods: Extension Methods Part

  • This is the sixth installment in my series of posts about extension methods. You can find links to the

  • Extension methods are a feature of the new C# language specification (also available in Visual Basic

  • Extension methods are a feature of the new C# language specification (also available in Visual Basic

  • Métodos de extensão são uma nova funcionalidade da especificação da linguagem C# (também disponível no

  • Métodos de extensão são uma nova funcionalidade da especificação da linguagem C# (também disponível no

  • can you write this extension methods in a report form

Page 1 of 1 (10 items)