Extension Methods

Extension Methods are a very cool and very innovative idea, new in Visual Studio 2008 (.NET Framework 3.5), that allows programmers to add custom methods to data types (without creating a derived type) that can be called just as if they were programmed within the type itself.

 

Whether it’s a class, structure, interface, delegate, etc. it can be extended, without inheritance, enabling it to gain new functionality, without actually having to be updated itself.

 

Imagine being able to add new functionality to the String data type so that it could change the case of it’s contents to Proper Case, maybe something like String.ToProper(); or imagine being able to add new functionality to the Integer data type so that it could raise it’s contents to a specified power, maybe something like Integer.ToPower(2);  or imagine being able to add new functionality to the DataTable data type so that when the Copy method is called, you could tell it exactly what columns of data you want copied, maybe something like DataTable.Copy(“Column1”, “Column2”), etc.  With Extension methods it’s now possible!  And one of the coolest things is, it’s easy.

 

 

Rules…

There are a some rules to consider before actually diving into working with Extension Methods, and they are: 

 

1.  An Extension Method can only be a Sub Procedure or a Function, not a property, field or event.

 

2.  An Extension Method must be marked with the <Extension()> attribute, located in the System.Runtime.CompilerServices namespace.

 

3.  An Extension Method must have at least 1 parameter.  This parameter is always the first parameter (when there are more than 1 parameter) that indicates the type that it is extending.  Additionally, this parameter is used to pass in the instance (ByVal or ByRef) of the data type it is extending.

 

4.  Extension Methods must be defined within a Module.

 

 

Getting Started…

Ok, so are you ready to see how this works?  Let’s go…

 

First, create a new Windows Forms project.  Next, add a new Module to it, named “ExtensionMethods.”  After the module has been added, Import the System.Runtime.CompilerServices namespace.  Your Module should now look like this:

 

Imports System.Runtime.CompilerServices

Module ExtensionMethods

End Module

 

 

String.ToProper()

Now we’re ready to add an Extension Method.  Let’s extend the String data type, and add a new method to it that will change it’s contents to Proper Case:

 

Remember that the Extension Method must be marked with <Extension()>, and the first parameter indicates what type is being extended.

 

Imports System.Runtime.CompilerServices

Module ExtensionMethods

    'Add the <Extension()> attribute.
    '  1st parameter indicates the type to extend
    '  (which is the String data type)
    <Extension()> _
    Public Function ToProper(ByVal str As String) _
        As String

        '"str" passes in the contents of the type
        '  it is extending.
        Return StrConv(str, VbStrConv.ProperCase)

    End Function

End
Module

 

Reminder:  Not only does the first parameter indicate what type is being extended, it is also used to pass in the instance (ByVal or ByRef) of the data type it is extending.

 

 

Integer.ToPower()

Ok, so that’s one Extension Method.  Let’s add another one, this time extending the Integer data type by adding a new method to it that will raise it’s value to the specified power:

 

    'Extend the Integer data type to raise it's
    '  contents to the specified power
    '
    '  1st parameter indicates the type to extend
    '  2nd parameter indicates the power
    <Extension()> _
    Public Function ToPower(ByVal int As Integer, _
        ByVal power As Integer) As Double

        Return Math.Pow(int, power)

    End Function

 

Note that the return value is a Double.  This is because the return value could be greater than an Integer, and the Math.Pow method returns a Double data type.

 

 

DataTable.Copy()

Let’s add one more Extension Method.  This time we will extend the DataTable data type by adding a new Copy method that will copy the contents of the DataTable to a new DataTable using the specified column names.

 

Method Overloading:  You may realize that the DataTable type already has a Copy() method that copies the contents of the DataTable to a new DataTable.  By adding a new Copy method that takes a parameter array of column names, we will actually be overloading the Copy method.  This is permissible, because it has a different (parameter) signature. 

 

   'Extend the DataTable data type to Copy it's
    '  contents to a new DataTable, but only
    '  copying the specifed columns
    <Extension()> _
    Public Function Copy( _
        ByVal table As DataTable, _
        ByVal ParamArray columnNames As String()) _
        As DataTable

        Return table.DefaultView _
                    .ToTable(False, columnNames)

    End Function

 
 

Complete ExtensionMethods Module Example

Now that we have our 3 Extension Methods in our new ExtensionMethods Module, let’s take a look at what the module looks like:

 

Imports System.Runtime.CompilerServices

Module ExtensionMethods

    'Add the <Extension()> attribute.
    '  1st parameter indicates the type to extend
    '  (which is the String data type)
    <Extension()> _
    Public Function ToProper(ByVal str As String) _
        As String

        '"str" passes in the contents of the type
        '  it is extending.
        Return StrConv(str, VbStrConv.ProperCase)

    End Function


    'Extend the Integer data type to raise it's
    '  contents to the specified power
    '
    '  1st parameter indicates the type to extend
    '  2nd parameter indicates the power
    <Extension()> _
    Public Function ToPower(ByVal int As Integer, _
        ByVal power As Integer) As Double

        Return Math.Pow(int, power)

    End Function


    'Extend the DataTable data type to Copy it's
    '  contents to a new DataTable, but only
    '  copying the specifed columns
    <Extension()> _
    Public Function Copy( _
        ByVal table As DataTable, _
        ByVal ParamArray columnNames As String()) _
        As DataTable

        Return table.DefaultView _
                    .ToTable(False, columnNames)

    End Function

End
Module

 

Testing it out…

Now that we have our Extension Methods defined, let’s test them out.

 

Go back to the form, add a button to it, then double-click the button to create the Button_Click event handler.  Test out the Extension Methods following my example below:

 

Private Sub Button1_Click( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles Button1.Click


    'Test our String Extension Method
    Dim str As String = "hello world!"

    'Displays "Hello World!"
    Console.WriteLine(str.ToProper())


    'Test our Integer Extension Method
    Dim int As Integer = 2

    'Displays "4"
    Console.WriteLine(int.ToPower(2))


    'Test our DataTable Extension Method
    Dim table As New DataTable()
    table.Columns.Add("ID")
    table.Columns.Add("Item")
    table.Columns.Add("ItemDescription")

    'Add 10 rows of data
    For i As Integer = 1 To 10

        table.Rows.Add(i, _
                "Item " & i, _
                i & " Item Description")

    Next


    'Copy the DataTable, but only copy
    '  the "Item" column, not the "ID" or
    '  "ItemDescription" columns
    Dim copiedTable As DataTable = _
        table.Copy("Item")


    'Loop through the rows in our copiedTable
    For Each row As DataRow In _
                        copiedTable.Rows

        'Ouput each column value
        For Each col As DataColumn In _
                        copiedTable.Columns

            Console.WriteLine(row(col))

        Next

    Next


    'Displays only the contents of the "Item"
    '  column


    'View Output Windows for results
    Stop


End
Sub

 

 

In the background…

If you were to look into the background and find out how the compiler is calling the extension methods, you would find that the compiler simply calls the shared method of the module, and passes it the variable calling the method.  So the compiler’s version below is equivalent to our example above, producing the exact same results:

 

    'String.ToProper()
    Console.WriteLine( _
        ExtensionMethods.ToProper(str))

    'Integer.ToPower(2)
    Console.WriteLine( _
        ExtensionMethods.ToPower(int, 2))

    'DataTable.Copy("Item")
    Dim copiedTable As DataTable = _
        ExtensionMethods.Copy(table, "Item")

 

This information is useful, because if you choose to, you can utilize this pattern for calling the ExtensionMethods.  Additionally, in rare situations where ambiguity occurs, this pattern will resolve conflicts.

 

 

Extending Interfaces…

As mentioned, interfaces can be extended as well as classes, structures and delegates, etc.  But extending interfaces is a little different, because instead of only defining the Extension Method, you have to actually implement it.  And if you are familiar at all with interfaces, immediately you’ll realize that is quite a bit different than the nature of interface types, which do not provide implementation.

 

Let’s take a look at an example of an IMath interface, and a ToPower() Extension Method:

 

Imports System.Runtime.CompilerServices


Public Interface IMath
End Interface


Module
ExtensionMethods

    'Extends the IMath interface,
    '  with Implementation.
    <Extension()> _
    Public Function ToPower(ByVal im As IMath, _
                    ByVal int As Integer, _
                    ByVal power As Integer) _
                    As Double

        Return Math.Pow(int, power)

    End Function

End
Module

 

And surprisingly enough, here’s how this example can be used in code:

 

Private Sub Button1_Click( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles Button1.Click


    Dim im As IMath

    'Displays "4"
    MsgBox(im.ToPower(2, 2))


End Sub

 

 

Importing…

If you put the module containing your Extension Methods in a namespace, then you will need to import that namespace in order to be able to access the Extension Methods.  That’s something to keep in mind, because if you try to access one of your Extension Methods through a type instance, and cannot find your Extension Method listed in Intellisense, that’s the reason.

 

Additionally, if you decide to create a library containing all of your Extension Methods, when you add it as a reference to another project, be sure to import the namespace as well.

 

 

Summary

Extension Methods allows custom methods to be added to data types.  The custom methods can only be Sub Procedures or Function, not properties, fields or events; they must be placed in a module; they must be marked with the System.Runtime.CompilerServices.Extension attribute; and they must have at least 1 parameter (the first parameter) that indicates the type that is being extended.

0 comments:

Leave a Reply

Translate

Google-Translate-Chinese (Simplified) BETA Google-Translate-English to French Google-Translate-English to German Google-Translate-English to Italian Google-Translate-English to Japanese BETA Google-Translate-English to Korean BETA Google-Translate-English to Russian BETA Google-Translate-English to Spanish

Tags