in

Foo Theory

Partners in Community - serving up some ice cold Kool-Aid!
Welcome to footheory.com.  The bloggers and contributing members on this site are consultants, project/program managers and software architects working across the US.  Our community will focus on Microsoft technologies, .NET architecture, software patterns & practices and just plain stream of consciousness.

Bennie's Weblog

New Features in C# 3.0. Part 4 : Extension Methods

Definition

An extension method is a method that you can use to extend the functionality of an existing type. This type can be an custom class that you have created, a standard BCL class such as System.String, or any interface, such as IEnumerable. Once you have created an extension method for the type, you can use the extension method as if it was a standard method of that type. From a client programmer's point of view, the extension method and the standard method of the type are indistinguishable.

Note: the code for this article can be downloaded here. The download contains a Orcas Beta 2 Solution. Each section in this post will name the appropriate project within the solution that applies to the section.

Steps to Create an Extension Method

If you want to extend an existing type with an extension method, follow these steps:

  1. Define a public static class.
  2. Define a public static method in this above class. The first method of this class should be the data type for which you want to define the extension method.
  3. Use the this keyword as the name of the first argument of the above method. The this keyword denotes the method as an extension method.

Examples

Example 1: User-Created class

Say you have written and deployed the following class:

1 public class MyMathClass 2 { 3 public double Add(double var1, double var2) 4 { 5 return var1 + var2; 6 } 7 8 public double Subtract(double var1, double var2) 9 { 10 return var1 - var2; 11 } 12 } // class MyMathClass

After the class has been successfully used in production, your customer asks you the add a Multiply method to your class. Since you do not want to re-deploy your entire application, you decide to write an extension method for the class, and deploy it in it's own assembly. Below is the code for the extension method:

1 /// <summary> 2 /// This is my Math Extensions class 3 /// </summary> 4 public static class MyMathExtensions 5 { 6 // The first argument of a class extension should always be named "this", and 7 // should have the Type of the Type you want to extend (in this class MyMathClass) 8 public static double Multiply(this MyMathClass math, double var1, double var2) 9 { 10 return var1 * var2; 11 12 } // method Multiply 13 14 } // class MyMathExtensions 15

As you can see, this method adheres to the rules defined for an extension method:

  • It is defined in a public, static class.
  • It is a static method
  • It's first argument is named this, and is of type MyMathClass, which is the type that we want to extend.

Below is a code sample that uses both the standard methods and the extension method, as you can see, the invocation method is the same for all 3 methods:

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 // The first example method uses the methods in the MyMath class 6 MyMathClass math = new MyMathClass(); 7 Console.WriteLine("5 + 10 = {0}", math.Add(5.0, 10.0)); 8 Console.WriteLine("10 - 2 = {0}", math.Subtract(10.0, 2.0)); 9 10 // This line used the "Multiply" extension method 11 Console.WriteLine("3 * 8 = {0}", math.Multiply(3, 8)); 12 13 } // method Main 14 } // class Program

The output is shown below:

5 + 10 = 15 10 - 2 = 8 3 * 8 = 24

You can find the code for this example in the UserDefinedClass project of the download solution.

Example 2: Extending a BCL class

In this example, we will take the System.Collections.ArrayList class, and extend it in two ways:

  • With the current API, a client can only insert a single value with the Insert method. We want to create an extension method which will overload the built-in Insert method to allow for the insertion of multiple values.
  • To view our results, we will also add a Print extension method to provide a clean printout of the values in our ArrayList instance.
Overloading the Insert method

The standard prototype of the insert method for the ArrayList class looks as follows:

void ArrayList,Insert(int index, object value);

so, we can insert a single value, but we have no ability to add a whole list of values, which might be desirable (you have the AddRange method, but that only allows for the addition of values at the end of  the ArrayList). Our extension method looks as follows:

1 // This class contains our Array List Extensions 2 public static class ArrayListExtensions 3 { 4 /// <summary> 5 /// This is our extension method for the Array class 6 /// </summary> 7 /// <param name="array"></param> 8 /// <param name="startIndex"></param> 9 /// <param name="values"></param> 10 public static void Insert(this ArrayList array, int startIndex, params object[] values) 11 { 12 // First, check the start index 13 if (startIndex > array.Count) 14 { 15 return; 16 } 17 18 // Add the values 19 foreach (object obj in values) 20 { 21 array.Insert(startIndex++, obj); 22 } 23 } // method Insert 24 ... 25 }// class ArrayListExtensions

So, this method uses the params keyword to allows for an arbitrary list of values to be inserted. The method verifies the validity of the startIndex, and then inserts each of the values, adjusting the startIndex after each insert.

The important fact to take away from this example is that you can use extension methods to overload existing methods of a type.

The Print extension method is shown below:

1 // This class contains our Array List Extensions 2 public static class ArrayListExtensions 3 { 4 .... 5 /// <summary> 6 /// This extension method prints out the content of the Array 7 /// </summary> 8 /// <param name="array"></param> 9 public static void Print(this ArrayList array) 10 { 11 Console.WriteLine("Array Contents"); 12 for (int i = 0; i < array.Count; i++) 13 { 14 Console.WriteLine("\tIndex: {0}\tValue: {1}", i, arrayIdea); 15 } 16 } // method Print 17 18 }// class ArrayListExtensions 19

A usage code sample is shown below (all of these examples are contained in the ExtendBCL project of the download code):

1 static void Main(string[] args) 2 { 3 // Create the array list, and use the "standard" 4 // method to insert a single element 5 ArrayList list = new ArrayList(); 6 list.Insert(0, 101); 7 8 // Now, use the extension method to insert a whole range 9 // of values 10 list.Insert(1, 10, 20, 30, 40, 50); 11 12 13 // Finally, use the Print extension method to 14 // print the content of the Array 15 list.Print(); 16 17 } // method Main

The above code invokes both the overloaded Insert method and the Print Extension method.

The output of this method is shown below:

Array Contents Index: 0 Value: 101 Index: 1 Value: 10 Index: 2 Value: 20 Index: 3 Value: 30 Index: 4 Value: 40 Index: 5 Value: 50

Note that our above example covered a non-generic collection type, but of course extension method can be applied to any type, including collection classes from the System.Collections.Generics namespace.

Example 3: Extending and Interface

You can also create extension methods for interface, be it your own interfaces, our BCL interface. For  example, below is a very simple extension method for IEnumerable<T>. The method returns the actual value from MoveNext() and is called MoveToNextElement():

1 /// <summary> 2 /// This class provides a simple extension method for "IEnumerator<T>" 3 /// </summary> 4 public static class IEnumeratorExtension 5 { 6 /// <summary> 7 /// This is a simple extension method that moves to the next 8 /// element of the enumerator, and returns the real value 9 /// instead of a boolean 10 /// </summary> 11 /// <typeparam name="T"></typeparam> 12 /// <param name="enumerator"></param> 13 /// <returns></returns> 14 public static T MoveToNextElement<T>(this IEnumerator<T> enumerator) 15 { 16 // Initialize our return value 17 T nextElement = default(T); 18 T currentElement = enumerator.Current; 19 20 // Try to move to the next element 21 if (enumerator.MoveNext()) 22 { 23 nextElement = enumerator.Current; 24 } 25 else 26 { 27 nextElement = currentElement; 28 } 29 30 return nextElement; 31 32 } // method MoveToNextElement<T> 33 }

Below is a small usage example:

1 static void Main(string[] args) 2 { 3 List<int> myInts = new List<int>{ 10, 20, 30, 40, 50 }; 4 5 // Get an enumerator for "myInts" 6 IEnumerator<int> enumerator = myInts.GetEnumerator(); 7 8 Console.WriteLine(enumerator.MoveToNextElement<int>()); 9 Console.WriteLine(enumerator.MoveToNextElement<int>()); 10 Console.WriteLine(enumerator.MoveToNextElement<int>()); 11 Console.WriteLine(enumerator.MoveToNextElement<int>()); 12 } 13

The output of this sample is shown below:

10 20 30 40

The above example also illustrates that extensions methods can be applied to generic types as well as "standard" types. The code for this section can be downloaded from the ExtendInterface project of the download code.

Extension method restrictions

The only restriction on extension methods is that they cannot be used to extend a static class, such as System.Math. This makes sense, since a static class cannot be represented by a "this" reference. For example, is you would try to write an extension method for the static System.Math class as shown below:

1 public static class MathExtension 2 { 3 public static int SumInits(this Math math, int value1, int value2) 4 { 5 return value1 + value2; 6 } 7 }

You would get the following compilation error:

Error 1 'System.Math': static types cannot be used as parameters InvalidExtensionMethod

This is illustrated in the InvalidExtensionMethod project of the download solution.

Intellisense support for Extension Methods

Beta2 of Orcas has very nice support for extension method. The extension method is listed as a method of the type, but is adorned with a special icon, and the tooltip includes the fact that it is an extension method, as is shown below:

Intel99

Note the small blue "down arrow" icon which indicates an extension method. Also, the tooltip clearly indicates that this method is an extension method by pre-fixing the signature with the (extension) keyword.

Usage of Extension Methods in LINQ

All LINQ operators, such as from, where, select etc. are implemented using extension methods. For example, the where clause checks for a boolean result of a test (e.g: Breed == "Great Dane") are implemented as extension methods. We will have more to say about this when we discuss lambda expressions.

Technorati Tags: , ,

Comments

 

Bennie's Weblog said:

Definition A Lambda Expression is basically a logic evolution of the concept of anonymous functions,

August 11, 2007 6:53 PM
 

Add custom method to File Class | keyongtech said:

Pingback from  Add custom method to File Class | keyongtech

April 17, 2009 9:07 AM

Leave a Comment

(required)  
(optional)
(required)  
Add

About bennie

I work for a Microsoft Gold Partner, Statera SouthWest as a Strategic Partner and a Solutions Architect
Copyright ASIQS Corporation © 2006, All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems