Scott Dorman

ephemeral segment

  Home  |   Contact  |   Syndication    |   Login
  603 Posts | 10 Stories | 862 Comments | 51 Trackbacks

News


Post Categories

Image Galleries


Microsoft Store


Creative Commons License



Locations of visitors to this page

Subscribers to this feed

TwitterCounter for @sdorman

View blog authority

Add to Technorati Favorites

Windows Live Alerts

AddThis Social Bookmark Button

LinkedIn profile

Community Credit profile

The Code Project

Follow me on Twitter

Get Free Shots from Snap.com

Community Credit Hall of Fame

Get Feedghost

Xobni outlook add-in for your inbox



Support This Site

Tag Cloud


Article Categories

Archives

Post Categories

Image Galleries

At one time or another most of us have wished we could add functions to one of the intrinsic .NET classes. The solution has always been to either create a static (or sealed) class that contains these "helper" functions or derive a new class that adds the desired functionality.

A good example of this is testing a string to see if it contains only alphanumeric characters. In order to do this, we need to define a helper class:

   1: namespace DataValidationHelpers
   2: {
   3:     public static class DataValidation
   4:     {
   5:         public static bool IsAlphanumeric(string expression) 
   6:         {
   7:             if (expression == null)
   8:             {
   9:                 throw new ArgumentNullException("expression");
  10:             }
  11:  
  12:             bool success = true;
  13:  
  14:             for (int i = 0; i < expression.Length; i++)
  15:             {
  16:                 if (!(Char.IsLetter(expression, i) || Char.IsNumber(expression, i) || Char.IsPunctuation(expression, i)
  17:                      || Char.GetUnicodeCategory(expression, i) == UnicodeCategory.SpaceSeparator))
  18:                 {
  19:                     success = false;
  20:                     break;
  21:                 }
  22:             }
  23:             return success;
  24:             }
  25:     }
  26: }

In order to use this method, we need to write code like

   1: string test = "Abc123";
   2: bool isAlphanumeric = DataValidationHelpers.DataValidation.IsAlphanumeric(test);

While this solution works, it always felt kludgy or incomplete. There had to be a better way, right? What we really wanted to be able to write was code like

   1: string test = "Abc123";
   2: bool isAlphanumeric = test.IsAlphanumeric();

Unfortunately, we weren't able to. That is, until .NET 3.5 and extension methods.

Extension methods allow a developer to add new methods to the public interface of an existing CLR type without deriving a new class or recompiling the original type. The decision to name this new class of function "extension methods" was a good choice as the name very clearly describes what these functions do. They "extend" an existing type by providing new functions that operate on that type without the need to recompile the original type, derive a new type, and allow extensions to types that are sealed which provides a natural syntax for making use of these extensions. Extension methods benefit from all of the compile-time checking you expect and also show up in the intellisense list:

image

In order to change this to an extension method, we need to make one change. That's right, one change. For C#, we need to add the "this" keyword before the first parameter. The "this" keyword in the function declaration instructs the compiler that the function extends the data type that immediately follows it. In our IsAlphanumeric function, we are telling the compiler that we are extending the String data type.

   1: namespace DataValidationHelpers
   2: {
   3:     public static class DataValidation
   4:     {
   5:         public static bool IsAlphanumeric(this string expression) 
   6:         {
   7:             if (expression == null)
   8:             {
   9:                 throw new ArgumentNullException("expression");
  10:             }
  11:  
  12:             bool success = true;
  13:  
  14:             for (int i = 0; i < expression.Length; i++)
  15:             {
  16:                 if (!(Char.IsLetter(expression, i) || Char.IsNumber(expression, i) || Char.IsPunctuation(expression, i) 
  17:                     || Char.GetUnicodeCategory(expression, i) == UnicodeCategory.SpaceSeparator))
  18:                 {
  19:                     success = false;
  20:                     break;
  21:                 }
  22:             }
  23:             return success;
  24:             }
  25:     }
  26: }

For VB, we add an Extension attribute to the method:

   1: Namespace DataValidationHelpers
   2:     Public Module DataValidation
   3:         <Extension()> _
   4:         Public Function IsAlphanumeric(ByVal expression As String) As Boolean
   5:             ' implementation
   6:         End Function
   7:     End Module
   8: End Namesapce

An interesting side effect is that we actually have two ways to call the same method:

   1: string test="Abc123";
   2: bool b1 = test.IsAphanumeric();
   3: bool b2 = DataValidation.IsAlphanumeric(test);

In fact, the IL code generated for either line is actually the same:

   1: L_0000: ldstr "Abc123"
   2:  L_0005: stloc.0 
   3:  L_0006: ldloc.0 
   4:  L_0007: call bool [Campari.Software.Core]Campari.Software.Text.DataValidation::IsAlphanumeric(string)
   5:  L_000c: pop 
   6:  L_000d: ldloc.0 
   7:  L_000e: call bool [Campari.Software.Core]Campari.Software.Text.DataValidation::IsAlphanumeric(string)
   8:  L_0013: pop 
   9:  L_0014: ret 

There are only a few things to keep in mind when writing extension methods.

  1. The only places you can define extension methods are a static class (C#) or a module (VB).
  2. You aren't actually adding methods to the class, you can only access the public members from your extension method. Logically, any extension methods that are defined on a type are available to any subclasses of that type.
  3. For the extension method to be available, you must include the namespace containing the extension.
  4. If the type you are extending has a real method that has the same name as the extension method, the extension method is ignored. This is important as it has comparability implications.
  5. Extension methods for properties are not possible.
  6. Extension properties are not possible.

Finally, even though there is no magic happening with extension methods, they do require the .NET Framework 3.5 in order to compile. You need to include a reference to System.Core (you will get a compiler error if you try to compile an extension method without including this reference), which is only available in the .NET Framework 3.5.

Extension methods help provide the power and flexibility of Linq and will provide a very significant shift in how we develop "helper" functions.

posted on Sunday, August 19, 2007 1:06 AM

Feedback

# re: C# 3.0 Extension Methods 8/19/2007 7:18 AM Daniel Moth
Hi Scott

This coverage reminded me a lot of mine here:
http://www.danielmoth.com/Blog/2007/02/extension-methods-c-30-and-vb9.html
...in particular the numeric list of things to remember is identical in content and order - great minds think alike :-)

Anyway, reason for commenting is to share that you can use extension methods in .NET 2.0 projects with a tiny hack:
http://www.danielmoth.com/Blog/2007/05/using-extension-methods-in-fx-20.html

Keep up the good work.

Cheers
Daniel

# re: C# 3.0 Extension Methods 8/19/2007 3:31 PM Scott
@Daniel,

I see what you mean about the list of steps being very similar. I guess there are only so many ways to talk about these things and still make it easy to understand.

Thanks for the tip about being able to use extension methods with .NET 2.0. It makes sense, since the attribute is empty and only needed at compile time. That really makes extension methods much more useful, since now they can be used "downlevel" as well.

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: