Extension Methods

Add Your Own Functions to Existing .NET Classes

Steve C Orr

October 30, 2009

10 Min Read
ITPro Today logo

ControlFreak

LANGUAGES:VB.NET | C#

ASP.NETVERSIONS: 3.5

 

Extension Methods

Add Your Own Functions to Existing .NET Classes

 

By Steve C. Orr

 

These days, it s easy for a developer to feel spoiled bythe seemingly infinite number of useful classes and methods included in the.NET Framework. A novice developer might even be forgiven for thinking they dnever need to add their own functionality to such a rich toolset even thoughexperienced developers know otherwise. However, even experienced developers mightnot agree on the best way to add such new features. For example, consider thefunction in Figure 1 that counts and returns the number of words contained in astring.

 

Public Function WordCount(ByVal s As String) As Integer

 Dim i As Integer

 Dim c = New Char() {""c, "."c, "!"c, "?"c}

 i =s.Split(c,StringSplitOptions.RemoveEmptyEntries).Length

 Return i

End Function

Figure 1: Thishomeless VB.NET function returns the number of words in a given string.

 

The remaining question is: Where should such a functiongo? Some reasonable choices are a public VB module or a static C# class. Itcould also go in a class library project for improved reusability. Grouping itwith a set of related functions is also a logical decision. One remainingconcern would then be discoverability. Your fellow developers cannot use thisfunction if they don t know it s there. The classic solution fordiscoverability documentation is nearly always a good idea, even thoughdocuments tend to be useful only when they are read by the right people at theright time. These days, the best programming documentation is the kind that sbuilt-in to development tools and automatically offered up at key moments such as Visual Studio IntelliSense.

 

To coax Visual Studio into offering up this string-relatedfunction at the right moments, it might be best to add it to the .NET Framework sString class. While it may be possible to get the source code for the Stringclass and add your own methods to it, such a relatively complex decision couldhave significant repercussions. A more object-oriented choice would be toinherit the .NET String class and extend it with custom functions. Unfortunately,many .NET classes are sealed, so this technique cannot be used consistently.

 

What Are Extension Methods?

Extension methods are effectively static functions thatcan be attached to any class. Thanks to the .NET 3.5 compilers and VisualStudio 2008, any class can be extended with new methods, even if the underlyingclass is sealed, pre-compiled, and thoroughly obfuscated. You can extend yourown classes, .NET classes, or even third-party classes. There is no need foraccess to the source code of the class.

 

To attach to the .NET Framework s String class theWordCount function shown in Figure 1, it merely needs to be prefixed with theExtension attribute of the System.Runtime.CompilerServices namespace (seeFigure 2).

 

_

Public Function WordCount(ByVal s As String) As Integer

 Dim i As Integer

 Dim c = New Char() {""c, "."c, "!"c, "?"c}

 i = s.Split(c,StringSplitOptions.RemoveEmptyEntries).Length

 Return i

End Function

Figure 2: TheExtension attribute added to the beginning of this VB.NET function assigns itto become a member of the String class.

 

Now the WordCount function will appear in Visual Studio2008 IntelliSense when you type a period after any string variable (see Figure3). Extension methods appear with a unique icon so you can tell them apart fromregular methods, even though you ll rarely need to care.

 

Figure 3: Anextension method as displayed by Visual Studio 2008 IntelliSense.

 

Mixing languages is no real problem, either. If desired,you can create a class library filled with extension methods in one language(such as C#), then add a reference to that assembly from any other .NET language.

 

What about C# Extension Methods?

Extension methods are supported by the C# compiler, aswell. Instead of prefixing functions with the Extension attribute, the this keyword must be used with the parameter and the function must be explicitlydeclared as static. Figure 4 shows the C# translation of the WordCount functionusing this alternate syntax.

 

public static int WordCount(this string s)

{

 var i = 0;

 var c = new char[] {" "[0], "."[0], "!"[0], "?"[0] };

 i=s.Split(c,StringSplitOptions.RemoveEmptyEntries).Length;

 return i;

}

Figure 4: Insteadof using the Extension attribute, C# developers use the this keyword with theparameter to specify extension methods.

 

Extension methods also can be a great way to bringlanguage-specific features in to the more global .NET world. For example, whendeveloping in C#, I often miss VB s IsNumeric function (as there is no directequivalent in C#). By adding a reference to Microsoft.VisualBasic.dll, anIsNumeric extension method can be added to the String class:

 

public static bool IsNumeric(this string s)

{

 returnMicrosoft.VisualBasic.CompilerServices.IsNumeric(s);

}

 

Because of the improved discoverability, even VBdevelopers can appreciate such an extension. Now all .NET developers can usethe same code to determine if a string contains a numeric value, regardless ofwhich language is being used:

 

return MyString.IsNumeric()

 

Further Improvements to the String Class

In the same vein, the usability of existing .NET Frameworkfunctions can be improved via extension methods. Consider the staticIsNullOrEmpty method that was added to the String class in version 2.0 of the.NET Framework. This simple one-line extension method can improve thereadability significantly:

 

public static bool IsNullOrEmpty(this string s)

{

 returnSystem.String.IsNullOrEmpty(s);

}

 

So now, instead of having to use this syntax:

 

String.IsNullOrEmpty(MyString)

 

you can use this more intuitive syntax:

 

MyString.IsNullOrEmpty()

 

By now your mind may be wandering about other useful methodsthat could be added to the String class to give it enhanced functionality. Forexample, how about adding some basic encryption functionality to the Stringclass? The following C# extension method hashes the attached string using theMD5 algorithm:

 

///

/// Hashes the string with the MD5 algorithm

///

/// A hashed version of the string

public static string MD5Hash(this string s)

{

 byte[] bDat = newUnicodeEncoding().GetBytes(s);

 byte[] bHash =

        new MD5CryptoServiceProvider().ComputeHash(bDat);

 returnBitConverter.ToString(bHash);

}

 

Encapsulate Regular Expressions

There are a virtually infinite number of useful regularexpressions to help developers match patterns and operate on strings. Memorizingthem all is even more impractical than trying to remember the complex syntaxrequired to construct your own. So once a developer has gone through all thework of finding (or piecing together) a needed regular expression, the resultis usually a valuable piece of work that should not be lost. This is anothergreat case for extension methods.

 

Consider a function that strips HTML tags from a string. Sucha function is not complicated; in fact, it can be done with a single line ofcode:

 

Regex.Replace(s, @"<(.|n)*?>", "") //Removes HTML

 

But rather than trying to remember this arcane syntax (andpotentially having to do a global search and replace if you ever decide tochange it) you can centralize the function in an intuitive way by extending theString class:

 

///

/// Removes any HTML markup from the string

///

/// An HTML-free version of the string

public static string StripHTML(this string s)

{

 return Regex.Replace(s,@"<(.|n)*?>", "");

}

 

Other common regular expressions can be encapsulated too,so they need not be memorized or scattered carelessly about. This C# extensionmethod determines whether the attached string object contains a syntacticallycorrect e-mail address:

 

///

/// Determines whether the string contains an email address

///

/// True if the string contains an email

public static bool IsEmailAddress(this string s)

{

 var regex = newRegex(@"^[w-.]+@([w-]+.)+[w-]{2,4}$");

 return regex.IsMatch(s);

}

 

Similarly, this next extension method determines whetheror not the attached string object contains a syntactically correct Web address:

 

///

/// Determines whether the string contains a URL

///

/// True if the string contains a URL

public static bool IsUrl(this string s)

{

 var reg =@"http(s)?://([w-]+.)+[w-]+(/[w- ./?%&=]*)?";

 Regex regex = newRegex(reg);

 return regex.IsMatch(s);

}

 

You could take this line of thought a step further by alsodetermining whether a syntactically correct Web address is also a real andactive Web page. The extension method in Figure 5 calls the previous IsUrlextension method to validate the string as syntactically correct, then attemptsto ping the URL.

 

///

/// Attempts to ping the string as a URL

///

/// True if the string is a responsive URL

public static bool IsUrlResponsive(this string s)

{

 if (s.IsUrl())

 {

  var p = newSystem.Net.NetworkInformation.Ping();

  s =s.ToLower().Trim().Replace("http://", "");

  s =s.Replace("https://", "");

  var pr = p.Send(s);

  return (pr.Status ==

      System.Net.NetworkInformation.IPStatus.Success);

 }

 return false;

}

Figure 5: Call theIsUrl extension method to ensure the string is syntactically correct, then tryto ping the URL.

 

Extend All Classes

While the String class is certainly ripe for extensionmethods, it is far from the only class that can benefit from them. The type ofclass being extended is specified by the parameter type passed to the extensionmethod. For example, the following extension method determines whether theattached integer is an even number or not:

 

///

/// Determines whether the number is even or odd

///

/// True if the number is even

public static bool IsEven(this int i)

{

 return((Math.IEEERemainder(i, 2) == 0));

}

 

And because it s so simple, you might as well throw in anIsOdd method, as well:

 

///

/// Determines whether the number is even or odd

///

/// True if the number is odd

public static bool IsOdd(this int i)

{

 return((Math.IEEERemainder(i, 2) != 0));

}

 

You can even add extension methods to the base Objectclass so that every class in the .NET Framework will be extended. Consider thisextension that adds an IsNull method to all objects:

 

///

/// Determines whether an object has been initialized

///

/// True if the object is initialized

public static bool IsNull(this object o)

{

 return (o == null);

}

 

Then you ll be able to write code like this:

 

If MyCol.IsNull() Then MyCol = New Collection()'VB.NET

 

You can override extension methods to create specialversions for certain classes if needed. For example, the following IsNullextension method specialized for the DateTime class replaces (and does notconflict with) the generic version previously listed:

 

///

/// Determines if a DateTime variable has been initialized

///

/// True if initialized

public static bool IsNull(this DateTime dt)

{

 if (dt == null) returntrue;

 return(dt==DateTime.MinValue || dt==DateTime.MaxValue);

}

 

This IsNull method extends the DateTime object withspecial business logic that considers a date to be null if it contains eitherthe minimum or maximum possible values.

 

Multiple Parameters

Extension methods can accept parameters just like anyother method can. The first parameter must always specify the type of classbeing extended, but any number of standard parameters may follow. To illustratethis fact, consider the method in Figure 6 that extends all string arrays.

 

///

/// Determines whether the array contains the string

///

/// string to search for

/// true if array contains the string

public static bool IsInArray(this string[] strings,string s)

{

 foreach (string str instrings)

 {

   if (s == str) returntrue;

 }

 return false;

}

Figure 6: Extend allstring arrays.

 

The first parameter in Figure 6 (decorated with the this keyword) specifies that all string arrays are being extended by this method. Thesecond parameter accepts the string to seek within the array. The extensionmethod returns true if the string value is found within the string array:

 

The method can be invoked with the following code:

string[] MyStrings = {"hello", "world"};//Create string array

return MyStrings.IsInArrary("world"); //Returns true

 

Conclusion

Extension methods are a simple and reliable way to addmethods to any existing class. I suspect a future version of the .NET Frameworkwill add concepts such as extension properties and extension events. In fact,extension properties and events actually came first as part of WindowsPresentation Foundation (WPF) and Silverlight. Curiously, extension methodswere not included. It seems inevitable that all such extensions will be unifiedas a part of the base .NET Framework at some point in the future.

 

The source codeaccompanying this article is available for download.

 

Steve C. Orr is anASP Insider, MCSD, Certified ScrumMaster, Microsoft MVP in ASP.NET, and authorof Beginning ASP.NET 2.0 AJAX (Wrox). He sbeen developing software solutions for leading companies in the Seattlearea for more than a decade. When he s not busy designing software systems orwriting about them, he can often be found loitering at local user groups andhabitually lurking in the ASP.NET newsgroup. Find out more about him at http://SteveOrr.net or e-mail him at mailto:[email protected].

 

 

 

Read more about:

Microsoft
Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like