C# 3.0: I like Extension Methods

After the declaration of C# 3.0 I went ahead and installed the PDC bits. After reading through the language spec. I was very very very unhappy. I mean we were just getting excited over C#2.0 supporting generics, anonymous mehtods, Nullable types and then suddenly someone ruins all the fun by showing us C#3.0. C#2.0 already appears stale. Some great blogs on this are from Cyrus and Matt Warren

The good part is that since I work in Microsoft, soon (after VS 2005 release I guess) we'd move to C#3.0 to dogfood it. So that means I'll not be required to wait for the super enhancements in C# for long!!!!

I thought I'd try out the features of C#3.0 and write some stuff on it. Here goes the second one (the first is already there)

Extension Methods

With extension methods you can attach additional functionalities to an existing type even if you do not have access to it. For example I can write an  extension method Print() which prints each element of any collection on a different line and then invoke it on any collection such as

 List<string> first = new List<string>();first.AddRange (new string[] {"Hello", "how"});first.Print(); 

Importantly note is that even though Print() is not a member of List<> but is still called as if its a method. There are a couple of restrictions in defining the extension method Print. All of which are marked in bold in the example below

 public static class Extensions{    public static void Print<T>(this ICollection<T> col)    {        foreach(T t in col)        Console.WriteLine(t);    }}static void Main(string[] args){     List<string> first = new List<string>();    first.AddRange (new string[] {"Hello", "how"});    first.Print(); }

The method has to be a static method in a static class. The first parameter to the method has to be qualified using this and this first parameter indicates the types on which this extension method can be applied. Its important to note that this is just syntactic sugar and both the following calls are equivalent and legal.

 first.Print();Extensions.Print(first);

So whats the benefit? I think this makes code more elegant and this can be used very much like C++ STL algorithms. Lets say I write a merge algorithm that merges two collections and then I'd be able to call this merge algorithm on any collection in a very elegant way. Following is the implementation of a merge algorithm

using

System;
using System.Collections.Generic;
// Will compile only with C#3.0 complilers
namespace ExtensionMethodDemo
{
public static class Extensions
{
public static void Merge<T>(this ICollection<T> first, ICollection<T> second)
{
foreach(T t in second)
first.Add(t);
}
}

    class Program
{
static void Main(string[] args)
{
List<string> first = new List<string>();
first.AddRange (new string[] {"Hello", "how"});

List<string> second = new List<string> ();
second.AddRange (new string[] {"are", "you", "doing"});

first.Merge(second);
}
}
}

Moreover the class libarary already ships with some standard extension methods (like standard algorithms of STL) and you can directly use them. Consider the following

 int[] a = new int[] {1, 2, 2, 4, 3};Console.WriteLine(a.Distinct().Count());

Here two of the methods Distinct and Count are used and the combined result is that we get the number of distinct elements in the array and that is 4. This is really really cool.

As a real life sample I wrote a pretty print extension method that prints the directory listing is a pretty fashion.

using

System;
using System.Collections.Generic;
using System.Text;
using System.Query;
using System.IO;

namespace ExtDemo
{   
public static class Extensions
{
public static void PrettyPrint(this IEnumerable<FileInfo> fInfo)
{
ConsoleColor defColor = Console.ForegroundColor;
            string format = "{0, -17} {1,10} {2}";
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(format, "Date", "Size (kb)", "File name");
Console.ForegroundColor = defColor;
            foreach(FileInfo file in fInfo)
Console.WriteLine(format, file.CreationTime.ToString("dd/MM/yyyy hh:mm"), (float)file.Length / 1024, file.Name);
}
}
    class Program
{
static void Main(string[] args)
{
DirectoryInfo dirInfo = new DirectoryInfo(@"c:\");
            dirInfo.GetFiles().PrettyPrint();
        }
}
}

The C# 3.0 spec clearly calls out that since this is not a very discoverable feature and is somewhat misleading (very much like operator overloading) and so should be used sparingly. I think that this'll be more and more used by class libraries to implement STL like algorithms that can be run on containers.