Thursday, August 21, 2008

C# Extension Methods

ScottGu has a quick introduction to C# Extension Methods that can be found here.   Extension Methods are a neat .NET 3.0 language feature that allows you to decorate an existing CLR class without having access to the code, or requiring a subclass.  Check out ScottGu's article for the good stuff.

I don't remember why I thought of the fact that the C# string object doesn't have a Reverse method (a.k.a. strrev), though you can probably think of [n] methods that other framework classes should have some method, or another.  So writing an extension method for the C# string object was my original foray into using extension methods.

public static string Reverse(this string inputString)
{
char[] response = new char[inputString.Length];
char[] inputChars = inputString.ToCharArray();

for (int i = (inputString.Length - 1), j = 0; i >= 0; i--, j++)
{
response[j] = inputChars[i];
}

return new string(response);
}

Which lets me do something very cool like:



string input = "Welcome to the Pink Palace!";

Debug.WriteLine("Input string : " + input);
Debug.WriteLine("Output string: " + input.Reverse());



 


The output of which is:


    Input string : Welcome to the Pink Palace!

    Output string: !ecalaP kniP eht ot emocleW


 


While that's pretty cool, it got me to thinking a little more.  I recently needed the ability to be able to filter whether a piece of code should handle an exception or not.  While thinking about the best way to handle that I thought I could apply an extension method to the Exception type and add the extension 'ShouldHandle'.  The result of which looks something like this:


 



public static bool ShouldHandle(this Exception eax, IEnumerable<string> exceptionList)
{

bool shouldHandle = false;
string exceptionType = eax.GetType().ToString();

foreach (string type in exceptionList)
{
if (type == exceptionType)
{
shouldHandle = true;
break;
}
}

return shouldHandle;
}






Then in the client code I'm thinking of, I can do something along the lines of:




try
{
throw new InvalidOperationException("test");
}
catch (Exception eax)
{
if (eax.ShouldHandle(exceptionList))
{
// handle the exception
//
}
else
{
throw;
}
}



 


I think the jury is still out on whether this code is more understandable than that helper library everyone is packing.  You know the one I'm talking about, MyCompany.Utilities.ReverseString, and the like.  I kinda like extension methods from a readability standpoint.  Extension methods usage is one of those things that may explode once it gets a little wider audience.


The complete code listing:




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace Shobu.Extensions
{

public static class MyExtensions
{
/// <summary>
/// Returns the reversed string.
/// </summary>
/// <param name="inputString">The instance of the string.</param>
/// <returns>The reversed string.</returns>
public static string Reverse(this string inputString)
{
char[] response = new char[inputString.Length];
char[] inputChars = inputString.ToCharArray();

for (int i = (inputString.Length - 1), j = 0; i >= 0; i--, j++)
{
response[j] = inputChars[i];
}

return new string(response);
}

/// <summary>
/// Determines whether or not the caller should handle the exception.
/// </summary>
/// <param name="eax">The exception to evaluate.</param>
/// <param name="exceptionList">The list of strings to copmare against.</param>
/// <returns>True, the caller should handle the exception. False, the caller should allow exception to bubble to another handler.</returns>
public static bool ShouldHandle(this Exception eax, IEnumerable<string> exceptionList)
{

bool shouldHandle = false;
string exceptionType = eax.GetType().ToString();

foreach (string type in exceptionList)
{
if (type == exceptionType)
{
shouldHandle = true;
break;
}
}

return shouldHandle;
}

}

class Program
{

static void Main(string[] args)
{


string input = "Welcome to the Pink Palace!";

Debug.WriteLine("Input string : " + input);
Debug.WriteLine("Output string: " + input.Reverse());


List<string> exceptionList = new List<string>
{
"System.Exception",
"System.ArgumentException",
"System.InvalidOperationException",
"System.ServiceModel.CommunicationException"
};



Exception e1 = new Exception("test 1");
Exception e2 = new ArgumentException("test 2");

// since we have extended the base class Exception, this is still valid
//
InvalidOperationException e3 = new InvalidOperationException("test 3");

Debug.WriteLine("should handle: " + e1.ShouldHandle(exceptionList));
Debug.WriteLine("should handle: " + e2.ShouldHandle(exceptionList));
Debug.WriteLine("should handle: " + e3.ShouldHandle(exceptionList));

try
{
throw new InvalidOperationException("test");
}
catch (Exception eax)
{
if (eax.ShouldHandle(exceptionList))
{
// handle the exception
//
}
else
{
throw;
}
}

}

}
}