Monday, February 04, 2008

C# 3.0 - New Language Features - Part 4

Extension Methods

Many times the classes that you are provided with (maybe as part of the BCL or someone else's API) wont have a method that you use often on instances of that class. For example - take the string class. It doesn't have a Right method that allows you to easily extract n characters from the right of that string. This would normally mean that you have to write a static method and call it - providing the string as one of the arguments.

The only problem with writing a static method that returns the n right most characters is that someone else who might work on your code might not realize that a function like that exists and might write duplicate code to perform the same function.

The way around this problem would be if we could write methods - that extend the set of methods provided by a class. Thus a method called Right would appear as one of the methods available on the string class objects.

The solution is now available as part of C# 3.0 and its called "Extension Methods".

Here is the Right method - that will return the right most n characters from a string - written as an extension method.

public static class ExtensionMethods
{
public static string Right(this string inString, int numChars)
{
if (inString == null)
return null;
if (numChars < 0 || numChars > inString.Length)
throw new ArgumentOutOfRangeException("numChars");
return inString.Substring(inString.Length - numChars, numChars);
}
}



Here is what you need to know to write an extension method.




  1. Extension methods need to be written in static classes.


  2. The first argument is of the type that the method is being extended on. The first argument is preceded by the keyword this.


  3. Extension methods are available only as part of C#3.x.


  4. Extension methods are a feature of the compiler and are not available at run-time.



The above extension method will be available on objects of string type. An additional requirement is that the namespace in which the extension method was declared must be available (either by a using statement or by declaration). The following shows how the extension method is used.




string sample = "The lazy brown fox";
Console.WriteLine(sample.Right(9)); //returns brown fox



The reason that I like extension methods is the fact that in VS 2008 - intellisense provides support to discover such methods - as shown in the following screen shot.



2 1



The image on the left shows intellisense showing all the member methods - as well as the extension method Right, that I defined. (Notice the blue down arrow - symbolizing an extension method). The image on the left shows the parameter info for the right method. (Again notice that the parameter info is shown as though Right is a member method of the string class - except for the prefix that tells you that it is in fact an extension method).



As extension methods are a compile time feature - basically - a shortcut for your code - what happens when the code is compiled is that the instance method syntax is substituted with the call to the actual static method. This is shown in the code below - which is the IL generated on a  call to the Right extension method. (See example above):




L_0001: ldstr "The lazy brown fox"
L_0006: stloc.0
L_0007: ldloc.0
L_0008: ldc.i4.s 9
L_000a: call string CSharp35FeaturesEM.ExtensionMethods::Right(string, int32)



Notes:

Clash between names of an existing method and an extension method with the same arguments:

So what happens if there is a clash in the names of the methods. In such a case - the method defined within the class takes precedence over the extension method with the same name. For example if I defined an extension method called Contains as follows:




public static bool Contains(this string inString)

Then this extension method will not be available for calling as instance method. (Though you could call it as a normal static method).


Clash between names of two extension methods with the same arguments:

In case of a clash in names between 2 extension methods on the same class - then a compiler error will be generated and the only way to call one the methods will be via the static method syntax.



public static class ExtensionMethods
{
public static string Left(this string inString, int numChars)
{
return "Left 1";
}
}

public static class ExtensionMethods2
{
public static string Left(this string inString, int numChars)
{
return "Left 2";
}
}



Trying to call the extension method Left as follows:

sample.Left(3); will result in a "error CS0121: The call is ambiguous..."



The only way to call one of the methods is by using static method syntax as follows:

ExtensionMethods.Left(sampleString, 3);

No comments: