Click here to Skip to main content
15,880,891 members
Articles / Programming Languages / C# 4.0

Extension Methods in .NET

Rate me:
Please Sign up or sign in to vote.
4.73/5 (62 votes)
29 Sep 2011CPOL4 min read 169.9K   1.2K   116   38
Introduction to extension methods in .NET with examples

Introduction

In this article, we will take a look at what extension methods are and how to use them in .NET. Personally, they are one of the best things that have been introduced into the .NET Framework in terms of readability. I will take you through what extension methods are, how to create them (in C# and VB), then I will show you some of the extension methods that I have created (in C# only, conversion is for you to try).

Contents

What are Extension Methods?

Extension methods allow you to easily extend a type, such as an integer or string, without re-compiling or modifying the type. In essence, they are a type of static (shared in VB) method, but they are called as if the method is native to the type. Extension methods are available from the 3.5 version of the .NET Framework and can be implemented on any type in the .NET Framework or any custom type that you define.

One downside to extension methods is if that you create an extension method with the same name as another method in that type, the compiler will bind the method call to the native method, not any extension. An extension method is only called when there is no native method found.

Warning

If you declare an extension method on the type Object, you will effectively create the extension method for every type in the framework including but not limited to String, Integer and Lists.

How Do We Create Extension Methods?

The basic outline of creating an extension methods goes something like this:

  1. Create a public static class (module in VB) 
  2. Define functions that you wish to perform
  3. Make the functions an extension method

Following through a complete example, I will now demonstrate how to create an extension method that returns the first 3 characters of a string. Using the list above, I must first create a static class or module:

C#
// C#
public static class Extensions
{

}
VB.NET
' VB
Module Extensions

End Module

The next phase would be to write the function that we are going to need, which in this case is the following:

C#
// C#
public static class Extensions
{
    public string GetFirstThreeCharacters(String str)
    {
        if(str.Length < 3)
        {
            return str;
        }
        else
        {
            return str.Substring(0,3);
        }
    }
}
VB.NET
' VB
Module Extensions

Public Function GetFirstThreeCharacters(Byval str As String) As String
    If (str.Length < 3) Then
        return str
    Else
        return str.SubString(0,3)
    End If
End Function

End Module

So far, we have done nothing special. The last phase is to make the functions' extension methods. It is slightly more complicated in VB, but not by much. I will deal with C# first.

To make our C# version of our function, we need an extension method to mark the function as static (so that it can be accessed at any time without the need for declaring anything) and secondly, mark the first parameter with the this keyword. This keyword basically tells the CLR that when this extension method is called, to use "this" parameter as the source. See the following:

C#
public static class Extensions
{
    public static string GetFirstThreeCharacters(this String str)
    {
        if(str.Length < 3)
        {
            return str;
        }
        else
        {
            return str.Substring(0,3);
        }
    }
}

Now for the VB version. Instead of using the this keyword, we need to do something slightly different. We need to mark the function with the System.Runtime.CompilerServices.Extension attribute like so:

VB.NET
<System.Runtime.CompilerServices.Extension> _
Public Function GetFirstThreeCharacters(Byval str As String) As String
If str.Length < 3 Then
	Return str
Else
	Return str.Substring(0, 3)
End If
End Function

If you copy this code into any project, you should be able to call it like so:

C#
// C#
String str = "my new String";
str = str.GetFirstThreeCharacters();
VB.NET
' VB
Dim str as String = "my new String"
str = str.GetFirstThreeCharacters()

As I explained for both languages above, the effective use of the this keyword makes the CLR take whatever we are calling the extension method from as the first parameter to our function.

Hint: Try adding an additional Integer parameter and using that as a replacement for the 0 in the code above.

Examples of Extension Methods

Here are a few of the extensions that I have found or created over time. These are helpful to me and I hope they are to you as well. If you have a question about any of these, drop me a comment below.

HasElements

Something that I often do is check a collection for a value. This method is designed to prevent me constantly checking for a null value and existence of any item in a given collection. This method will work on any collection that implements the ICollection interface.

Definition:

C#
/// <summary>
/// Determines whether the specified collection has any elements in the sequence.
/// This method also checks for a null collection.
/// </summary>
/// <param name="items">The ICollection of items to check.</param>
public static bool HasElements(this ICollection items)
{
    return items != null && items.Count > 0;
}

Example usage:

C#
List<String> myList = new List<String>();
if (myList.HasElements())
{
    // do some code
}

IsBetween

The IsBetween method returns a boolean and determines whether or not a value is between an inclusive upper and lower boundary. This will only work on types that implement the IComparable interface.

Definition:

C#
/// <summary>
/// Determines whether a value is between a minimum and maximum value.
/// </summary>
/// <typeparam name="T">The type of the value parameter.</typeparam>
/// <param name="value">The value that needs to be checked.</param>
/// <param name="low">The inclusive lower boundary.</param>
/// <param name="high">The inclusive upper boundary.</param>
public static bool IsBetween<T>(this T value, T low, T high) where T : IComparable<T>
{
    return value.CompareTo(low) >= 0 && value.CompareTo(high) <= 0;
}

Example usage:

C#
Int32 myInt = 0;
myInt.IsBetween(0, 5); // returns true
myInt.IsBetween(1, 5); // returns false

Each

Quite often, I have to perform a task on a collection of items. This is just a shortcut way for saying for each element in the collection, perform this action. This will work on any collection that implements the ICollection interface. The action that is parsed in can be a lambda expression or a function/subroutine.

Definition:

C#
/// <summary>
/// Executes the given action against the given ICollection instance.
/// </summary>
/// <typeparam name="T">The type of the ICollection parameter.</typeparam>
/// <param name="items">The collection the action is performed against.</param>
/// <param name="action">The action that is performed on each item.</param>
public static void Each<T>(this ICollection<T> items, Action<T> action)
{
    foreach (T item in items)
    {
        action(item);
    }
}

Example usage:

C#
List<String> myList = new List<String>();
myList.Each(el =>
{
    // perform an action(s) on the item
    el.Substring(0,1);
    el = el;
});

In

Often it is necessary to determine whether a value is in a set collection. For example, I need to check whether a string is in an allowed list. This method will allow us to check any value against an array of values of the same type.

Definition:

C#
/// <summary>
/// Determines whether a parameter is in a given list of parameters.
/// E.g.. 11.In(1,2,3) will return false.
/// </summary>
/// <typeparam name="T">The type of the source parameter.</typeparam>
/// <param name="source">The item that needs to be checked.</param>
/// <param name="list">The list that will be checked for the given source.</param>
public static bool In<T>(this T source, params T[] list)
{
    if (null == source) throw new ArgumentNullException("source");
    return list.Contains(source);
}

Example usage:

C#
Int32 myInt = 0;
myInt.In(0, 0, 1, 2, 3); // returns true
myInt.In(1, 5, 6, 7, 8); // returns false

Hopefully, you now have an understanding of how to implement extension methods in both C# and VB.NET. If you need any help with your extension methods or if you would like to ask a question, drop me a comment below.

Related Links

History

  • 28th September, 2011: Initial version
  • 29th September, 2011: Fixed a compiler bug in the article's text. Source code is fine.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United Kingdom United Kingdom
I am a self-taught programmer originating from the Isle of Wight. I have approximately 3 years commercial experience in developing LOB applications for a wide range of customers (local companies to FTSE 100). During this time, I have honed my development skills across a wide range of platforms and frameworks including C#, VB.net, T-SQL, Web Services and Android programming.

I am currently training towards my MCSA SQL Server 2012 qualification alongside my degree in Computing from Bournemouth University (Graduating 2013). During my time at Bournemouth University, I have built an extensive knowledge in core networking protocols and architecture including TCP, IP (v4 & v6), DNS, ARP, HTTP, EMAIL (SMTP, POP3 and IMAP), WiMAX (802.16) and WiFi (802.11).

I like to help fellow developers through the StackExchange network, accumulating approximately 3000 reputation across the various sites. I also like to maintain a technology blog to help others learn.

Comments and Discussions

 
SuggestionMy Vote Pin
Vishal Avhad28-Nov-13 18:12
Vishal Avhad28-Nov-13 18:12 
QuestionExtension methods Pin
Wisen Technologies10-Aug-13 10:09
Wisen Technologies10-Aug-13 10:09 
GeneralMy vote of 5 Pin
Rajeev Jayaram3-Jul-13 3:30
Rajeev Jayaram3-Jul-13 3:30 
QuestionGood post Pin
SachinThunder28-Jun-13 3:42
SachinThunder28-Jun-13 3:42 
GeneralMy vote of 4 Pin
Raju_B27-Jan-13 20:07
Raju_B27-Jan-13 20:07 
GeneralMy vote of 5 Pin
shekar.net8-Nov-12 0:23
shekar.net8-Nov-12 0:23 
GeneralMy vote of 4 Pin
ajayendra270710-Oct-12 21:47
ajayendra270710-Oct-12 21:47 
GeneralMy vote of 5 Pin
karenpayne6-Aug-12 11:36
karenpayne6-Aug-12 11:36 
QuestionIn addition to your information Pin
karenpayne6-Aug-12 11:34
karenpayne6-Aug-12 11:34 
Just wanted to add some ideas to the information presented.
One of my main usages for extension methods is to go method based rather than function based. For example instead of

VB
Private Sub Demo()
    If cn.State = ConnectionState.Closed Then
        '
        '
        '
    End If
End Sub


Utilize an extension
VB
Private Sub Demo()
    If cn.IsClosed Then
        '
        '
        '
    End If
End Sub


Underlying extension
VB
<System.Diagnostics.DebuggerStepThrough()> _
<Runtime.CompilerServices.Extension()> _
Public Function IsClosed(ByVal sender As OleDb.OleDbConnection) As Boolean
    Return sender.State = ConnectionState.Closed
End Function


Personally this makes more sense and is easier on the eye. Once I find a useful extension method commonly used I will place it in a class project then add the resulting dll to a project that will use the extension methods. The caveat here is to mark modules public so the methods are visible outside the class project. For instance, I generally manually create a BindingSource and assign a DataTable as the DataSource in say form load event of a win form project. Later I can use

VB
Dim dtCustomers As DataTable = bsTaxpayers.DataTable


The extension
VB
Imports System.Windows.Forms
Public Module BindingSourceExtensions
    <System.Diagnostics.DebuggerStepThrough()> _
    <System.Runtime.CompilerServices.Extension()> _
    Public Function DataTable(ByVal sender As BindingSource) As DataTable
        Return DirectCast(sender.DataSource, DataTable)
    End Function
End Module


From the DataTable perhaps we want the value in the last row of the DataTable.
VB
Dim SomeValue As Integer = bsTaxpayers.DataTable.LastValue(Of Int32)("SomeField")

VB
Dim SomeValue As String = bsTaxpayers.DataTable.LastValue(Of String)("SomeField")


For the above the column name is used but there many be times to use an ordinal position. To me this is where a good design comes into play so create a version for ordinal position too.

VB
Public Module DataTableExtensions
    <System.Diagnostics.DebuggerStepThrough()> _
    <System.Runtime.CompilerServices.Extension()> _
    Public Function LastValue(Of T)(ByVal dt As DataTable, ByVal ColumnName As String) As T
        Return dt.Rows.Item(dt.RowCount).Field(Of T)(dt.Columns(ColumnName))
    End Function
    <System.Diagnostics.DebuggerStepThrough()> _
    <System.Runtime.CompilerServices.Extension()> _
    Public Function LastValue(Of T)(ByVal dt As DataTable, ByVal ColumnIndex As Integer) As T
        Return dt.Rows.Item(dt.RowCount).Field(Of T)(dt.Columns(ColumnIndex))
    End Function
    <System.Diagnostics.DebuggerStepThrough()> _
    <System.Runtime.CompilerServices.Extension()> _
    Public Function RowCount(ByVal dt As DataTable) As Integer
        Return dt.Rows.Count - 1
    End Function
End Module


Note in the above extensions we use another extension RowCount. Also LastValue is done similarly to Field extension method a standard extension.
VB
Dim MyResult =
    (
        From T In bsTaxpayers.DataTable.AsEnumerable
        Where T.Field(Of String)("SomeField") = "HELLO"
    ).FirstOrDefault



One last idea is to create extension methods that make it easier for a developer to work out a solution. Write the extension and include help/directions as shown below.

VB
''' <summary>
'''
''' </summary>
''' <param name="sender"></param>
''' <param name="MasterTableName"></param>
''' <param name="ChildTableName"></param>
''' <param name="KeyColumn">Identifying column between master and child table</param>
''' <param name="Name">Relationship name</param>
''' <remarks></remarks>
<System.Runtime.CompilerServices.Extension()> _
Public Sub SetRelation(ByVal sender As DataSet, ByVal MasterTableName As String, ByVal ChildTableName As String, ByVal KeyColumn As String, ByVal Name As String)
   sender.Relations.Add( _
      New DataRelation(Name, _
         sender.Tables(MasterTableName).Columns(KeyColumn), _
         sender.Tables(ChildTableName).Columns(KeyColumn) _
      ) _
   )
End Sub

Kevin S. Gallagher
Programming is an art form that fights back

QuestionExample for Each does nothing useful. Pin
Philippe Mori3-Jul-12 12:41
Philippe Mori3-Jul-12 12:41 
GeneralMy vote of 5 Pin
hypermellow23-May-12 0:34
professionalhypermellow23-May-12 0:34 
GeneralMy vote of 3 Pin
FrewCen9-Apr-12 19:28
FrewCen9-Apr-12 19:28 
GeneralMy vote of 5 Pin
  Forogar  27-Oct-11 11:21
professional  Forogar  27-Oct-11 11:21 
QuestionNice Article But Have A Quick Question Pin
supernova566620-Oct-11 1:41
supernova566620-Oct-11 1:41 
AnswerRe: Nice Article But Have A Quick Question Pin
Stuart Blackler20-Oct-11 2:10
Stuart Blackler20-Oct-11 2:10 
GeneralThanks my vote of 5 Pin
Vivek_Minhas9-Oct-11 22:20
Vivek_Minhas9-Oct-11 22:20 
GeneralMy vote of 4 Pin
MicroImaging7-Oct-11 13:40
MicroImaging7-Oct-11 13:40 
GeneralMy vote of 5 Pin
Brian Stevens4-Oct-11 23:33
Brian Stevens4-Oct-11 23:33 
QuestionVery Nice! Pin
CircusUgly4-Oct-11 8:19
CircusUgly4-Oct-11 8:19 
AnswerRe: Very Nice! Pin
Stuart Blackler4-Oct-11 11:08
Stuart Blackler4-Oct-11 11:08 
QuestionNull check necessary? Pin
R. Hoffmann3-Oct-11 22:28
professionalR. Hoffmann3-Oct-11 22:28 
AnswerRe: Null check necessary? Pin
Stuart Blackler3-Oct-11 23:57
Stuart Blackler3-Oct-11 23:57 
GeneralRe: Null check necessary? Pin
R. Hoffmann4-Oct-11 0:09
professionalR. Hoffmann4-Oct-11 0:09 
GeneralRe: Null check necessary? Pin
bjarneds4-Oct-11 9:34
bjarneds4-Oct-11 9:34 
GeneralRe: Null check necessary? Pin
Stuart Blackler4-Oct-11 11:07
Stuart Blackler4-Oct-11 11:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.