Tuesday, October 24, 2006

In my opinion, one of the most marginalized features of C# 3.0 are extension methods. The idea behind them is to provide a way to extend a class with new methods by allowing static methods to be called using instance method syntax. Here's a simple example of how they can be used:

using System;

namespace ExtensionTest
{
  static class Program
  {
    static int ToInt32(this string s)
    {
      return Int32.Parse(s);
    }

    static void Main(string[] args)
    {
      string text = "1000";
      int textValue = text.ToInt32() + 1;
      Console.WriteLine(textValue);
    }
  }
}

The above code declares a ToInt32() extension method for the System.String class. In the Main() method, ToInt32() is called using instance method syntax on a local string variable. As expected, this program prints 1001 to the console when run.

One interesting side effect of extension methods is that they can be called on a null reference without raising a NullReferenceException. Let's expand the sample code a bit to show what I mean:

using System;

namespace ExtensionTest
{
  static class Program
  {
    static int ToInt32(this string s)
    {
      if (String.IsNullOrEmpty(s))
        return 0;

      return Int32.Parse(s);
    }

    static void Main(string[] args)
    {
      string text = null;
      int textValue = text.ToInt32() + 1;
      Console.WriteLine(textValue);
    }
  }
}

The updated sample doesn't raise a NullReferenceException when text.ToInt32() is called—even though text is a null reference. Instead, the method returns 0 and 1 is printed to the console.

Obviously, this side effect can be a bit of a land mine and it is good practice to allow NullReferenceExceptions to be thrown from extension methods so that everything works as expected. But, there are a couple of cases where this side effect can be used to great effect.

Over at Phil Haack's blog, there is a bit of discussion about the String.IsNullOrEmpty static method that was added in .NET Framework 2.0. In the comments section, Jon Galloway wrote the following: "I like the static method, but I wish there were a member method (which just called the static) as well. I'm always thrown when myString.IsNullOrEmpty() isn't defined..." The next comment states that this doesn't make sense because you can't call an instance method on a null reference. However, you can call an extension method on a null reference. Here is how you could achieve a String.IsNullOrEmpty() instance method that really works:

using System;

namespace StringExtensionTest
{
  static class Program
  {
    static bool IsNullOrEmpty(this string s)
    {
      return (s == null || s.Length == 0);
    }

    static void Main(string[] args)
    {
      string nullText = null;
      string emptyText = String.Empty;
      string realText = "Dustin has a trophy wife";

      Console.WriteLine("nullText.IsNullOrEmpty: {0}", nullText.IsNullOrEmpty());
      Console.WriteLine("emptyText.IsNullOrEmpty: {0}", emptyText.IsNullOrEmpty());
      Console.WriteLine("realText.IsNullOrEmpty: {0}", realText.IsNullOrEmpty());
    }
  }
}

Thanks to the null-reference side effect of extension methods, the above program writes this to the console:

nullText.IsNullOrEmpty: True
emptyText.IsNullOrEmpty: True
realText.IsNullOrEmpty: False

In my opinion, taking advantage of the side effect in this case is OK because the name of the method is descriptive. In addition, this allows us to add another method to the .NET framework that is sorely missing: Array.IsNullOrEmpty():

using System;

namespace ArrayExtensionTest
{
  static class Program
  {
    static bool IsNullOrEmpty(this Array array)
    {
      return (array == null || array.Length == 0);
    }

    static void Main(string[] args)
    {
      string[] nullArray = null;
      string[] emptyArray = { };
      string[] realArray = { "Dustin", "Dustin's Trophy Wife" };

      Console.WriteLine("nullArray.IsNullOrEmpty: {0}", nullArray.IsNullOrEmpty());
      Console.WriteLine("emptyArray.IsNullOrEmpty: {0}", emptyArray.IsNullOrEmpty());
      Console.WriteLine("realArray.IsNullOrEmpty: {0}", realArray.IsNullOrEmpty());
    }
  }
}

For my own forays into C# 3.0, I've placed both of these extension methods inside the System namespace. That way, all I have to do is reference the assembly containing my extensions and they will always be available:

namespace System
{
  public static class SystemExtensions
  {
    public static bool IsNullOrEmpty(this Array array)
    {
      return (array == null || array.Length == 0);
    }
    public static bool IsNullOrEmpty(this string s)
    {
      return (s == null || s.Length == 0);
    }
  }
}

kick it on DotNetKicks.com

posted on Tuesday, October 24, 2006 10:13:49 AM (Pacific Standard Time, UTC-08:00)  #    Comments [6]

kick it on DotNetKicks.com