Understanding Extension Methods

Intertech Tutorials

Understanding Extension Methods

As you know, once a type is defined and compiled into a .NET assembly, its definition is, more or less, final. The only way to add new members, update members, or remove members is to recode and recompile the code base into an updated assembly. Or you could take more drastic measures such as using the System. Reflection.Emit namespace to reshape a compiled type dynamically.

Extension methods allow you to extend the functionality of previously compiled types. Using extension methods, you can add functionality to precompiled types while providing the illusion these methods were that all along.

When you create extension methods, the existing precompiled assembly is not literally modified. Rather, the type is extended within the current project. If you package extension methods into a custom .NET *.dll, other applications would need to reference this library to make use of the extensions. To this end, extension methods are really just a way to ‘pretend’ a type has new functionality. The real type is not modified in any way.

This technique can be quite helpful when you need to inject new functionality into types for which you do not have an existing code base. It can also be quite helpful when you need to force a type to support a set of members (for the interest of polymorphism) but cannot modify the original type declaration. Also, LINQ technologies make use of extension methods to integrate query expression support into the .NET base class libraries.

In C#, extension methods can only be defined within a static class. All extension methods are marked as such by using the this keyword as a modifier on the first (and only the first) parameter of the method in question. This parameter represents the data type being extended. Once implemented, extension methods can be called either from the correct instance in memory or statically via the defining static class.

In VB, extension methods must be marked with the <Extension> attribute. This attribute is defined within the System.Runtime.CompilerServices namespace. Also, VB extension methods can only be defined in Module types, not Class or Structure types.

Assume you are authoring a utility class named MyExtensions that defines a single extension method. The method extends System.Object with a brand new method named DisplayDefiningAssembly(). Given that Object is the parent to all .NET types, all types now have this new method.


// C#
using System.Reflection;

static class MyExtensions
{
// This method allows any object to display the assembly
// in which it is defined.
public static void DisplayDefiningAssembly(this object obj)
{
Console.WriteLine("{0} lives here: {1}", obj.GetType().Name,
Assembly.GetAssembly(obj.GetType()));
}
}




' VB
Imports System.Runtime.CompilerServices
Imports System.Reflection

Public Module MyExtensions
' This method allows any object to display the assembly
' in which it is defined.
<Extension()>_
Sub DisplayDefiningAssembly(ByVal obj As Object)
Console.WriteLine("{0} lives here: {1}", obj.GetType().Name, _
Assembly.GetAssembly(obj.GetType()))
End Sub
End Module

The first parameter of an extension method always denotes the type being extended. Of course, a given extension method could have multiple parameters. To illustrate, here is an overloaded extension method for the System.Int32 structure:


// C#
static class MyExtensions
{
...
// Every Int32 now has a Foo() method ...
public static void Foo(this int i)
{ Console.WriteLine("{0} called the Foo() method.", i); }

// ...which has been overloaded to take a string!
public static void Foo(this int i, string msg)
{ Console.WriteLine("{0} called Foo() and told me: {1}", i, msg); }
}



' VB
Module MyExtensions
...
' Every Int32 now has a Foo() method ...
<Extension()> Public Sub Foo(ByVal i As Integer)
Console.WriteLine("{0} called the Foo() method.", i)
End Sub

' ...which has been overloaded to take a string!
<Extension()>Public Sub Foo(ByVal i As Integer, _
  ByVal msg As String)
Console.WriteLine("{0} called Foo() and told me: {1}", i, msg)
End Sub
End Module

Now consider the following usage. The VB code differs primarily by the declaration of the variables (via the Dim keyword) and removal of semicolons.


// C#
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Extension Methods *****\n");

// The int has assumed a new identity!
int myInt = 12345678;
myInt.DisplayDefiningAssembly();

// So has the DataSet!
System.Data.DataSet ds = new System.Data.DataSet();
ds.DisplayDefiningAssembly();

// And the SoundPlayer!
System.Media.SoundPlayer sp = new System.Media.SoundPlayer();
sp.DisplayDefiningAssembly();

// Use new integer functionality.
myInt.Foo();
myInt.Foo("Ints that Foo? Who would have thought it!");

// This would be an error! Booleans don’t have the Foo() method!
// bool b2 = true;
// b2.Foo();
}

linq4-495x110

Be aware that Visual Studio’s IntelliSense will show you which members are extension methods. Look for the icon with a blue downward arrow. Notice in the following screen shot, DisplayDefiningAssembly() and Foo() have the new extension method icon.


Copyright (c) 2008-2013. Intertech, Inc. All Rights Reserved. This information is to be used exclusively as an online learning aid. Any attempts to copy, reproduce, or use for training is strictly prohibited.