Saturday, November 25, 2006

A reader added the following comment to my Neat Tricks With Extension Methods article:

Is it possible to make something like :

namespace System
{
  public static class SystemExtensions<T> where T
    : Something // Array and String implements Length property but not from an interface so...
  {
    public static bool IsNullOrEmpty(this T obj)
    {
      return (obj == null || obj.Length == 0);
    }
  }
}

Unfortunately, the answer is, no, you can't quite do that. There are two problems here: 1) it's useless to add a generic parameter to a static class because it can never be instantiated (since it's static) and 2) the generic constraint language isn't rich enough to ensure that methods or properties are available on a generic argument unless they already exist in an implemented interface. The solution to the first problem is to make the method generic and not the class. However, the second problem makes this impossible since String and Array don't implement an interface that exposes the Length property. If they did, we could write something like this:

namespace System
{
  public static class SystemExtensions
  {
    public static bool IsNullOrEmpty(this IHasLength obj)
    {
      return (obj == null || obj.Length == 0);
    }
  }
}

While the reader's request isn't really possible, it did lead me to realize a limitation of the Array.IsNullOrEmpty method that I presented in the previous article. Originally, I had written an extension method called IsNullOrEmpty that worked with arrays. However, it makes sense to broaden the use of this extension method by making it target ICollection like this:

using System.Collections;

namespace System
{
  public static class SystemExtensions
  {
    public static bool IsNullOrEmpty(this ICollection obj)
    {
      return (obj == null || obj.Count == 0);
    }
  }
}

Written that way, our extension method not only works with arrays, but any .NET collection as well!

using System;
using System.Collections;
using System.Collections.Generic;

namespace ExtensionMethodTests
{
  static class Program
  {
   public static bool IsNullOrEmpty(this ICollection c)
   {
     return (c == null || c.Count == 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());

     Console.WriteLine();

     Stack nullStack = null;
     Stack emptyStack = new Stack();
     Stack realStack = new Stack(new string[] { "Dustin", "Dustin's Trophy Wife" });

     Console.WriteLine("nullStack.IsNullOrEmpty: {0}", nullStack.IsNullOrEmpty());
     Console.WriteLine("emptyStack.IsNullOrEmpty: {0}", emptyStack.IsNullOrEmpty());
     Console.WriteLine("realStack.IsNullOrEmpty: {0}", realStack.IsNullOrEmpty());

     Console.WriteLine();

     List<string> nullList = null;
     List<string> emptyList = new List<string>();
     List<string> realList = new List<string>(new string[] { "Dustin", "Dustin's Trophy Wife" });

     Console.WriteLine("nullList.IsNullOrEmpty: {0}", nullList.IsNullOrEmpty());
     Console.WriteLine("emptyList.IsNullOrEmpty: {0}", emptyList.IsNullOrEmpty());
     Console.WriteLine("realList.IsNullOrEmpty: {0}", realList.IsNullOrEmpty());
   }
  }
}

Happy spelunking!

posted on Saturday, November 25, 2006 7:12:43 AM (Pacific Standard Time, UTC-08:00)  #    Comments [1]

kick it on DotNetKicks.com