Three levels of extension methods


Share

Since the release of C# 3.0, I’ve been guilty of viewing every problem as an extension method problem. I can’t blame me, considering how much clarity I can add to my code with judicious use of a construct that provides the ability to invoke static methods on instances of their types, decorating closed classes as I see fit. Having used them for awhile, I have found that there are really three kinds of extension methods, and that it’s probably healthy to strive to use the third level whenever possible.

Level one: Rewriting static framework methods as extension methods

Somewhere near the beginning of your extension method honeymoon you’re going to realize something: Any static method in the .NET Framework could (nay, must!) have an extension method. In some cases, like String.Format, that’s very useful:

public static class StringExtensions
{
    public static string Format(this string format, params object[] args)
    {
        return String.Format(format, args);
    }
}

Now we can format strings on the instance, and don’t need to define separate methods in all of our classes when we want to accomplish something similar:

“{0}, {1}, and {2}”.Format(first, second, third);

Of course, the Math and Convert classes need similar treatment. We can turn this headache of an inline calculation method:

decimal value = 42
double sin = Math.Sin(Math.ToRadians(Convert.ToDouble(radians)));

Into something we can actually read:

value.ToRadians().Sin();

It’s only a matter of time before someone writes an extension for every possible static class in .NET and offers it up for the greater good, but I hope that it comes with the ability to pick and choose the namespaces, otherwise it might get tiring carrying that huge library around just to make occasional use of our static class replacements.

Level two: Creating new extension methods to solve common problems

My favorite extension method so far is one that safely attempts to parse a String into a Guid, and returns the result:

public static bool IsNotNullOrEmpty(this string input) 

    return !String.IsNullOrEmpty(input); 
}
 
public static bool IsRegexMatch(this string input, string pattern)
{
    return Regex.IsMatch(input, pattern);
}

public static bool IsRegexMatch(this string input, string pattern, RegexOptions options)
{
    return Regex.IsMatch(input, pattern, options);
}

public static bool IsRegexMatchAny(this string input, params string[] patterns)
{
    var isMatch = false;
    foreach (string pattern in patterns)
    {
        isMatch |= input.IsRegexMatch(pattern);
    }
    return isMatch;
}

public
 static bool IsRegexMatchAny(this string input, RegexOptions options, params string[] patterns)
{
    var isMatch = false;
    foreach (string pattern in patterns)
    {
        isMatch |= input.IsRegexMatch(pattern, options);
    }
    return isMatch;
}

public
 static bool IsRegexMatchAll(this string input, params string[] patterns)
{
    var isMatch = true;
    foreach (string pattern in patterns)
    {
        isMatch &= input.IsRegexMatch(pattern);
    }
    return isMatch;
}

public static bool IsRegexMatchAll(this string input, RegexOptions options, params string[] patterns)
{
    var isMatch = true;
    foreach (string pattern in patterns)
    {
        isMatch &= input.IsRegexMatch(pattern, options);
    }
    return isMatch;
}

 
public static Guid ToGuid(this string input)
{
     var guid = @”^({){0,1}[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]” +
an style="color: maroon">           @”{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(}){0,1}$”;
 
return input.IsNotNullOrEmpty() && input.IsRegexMatch(guid) ? new Guid(input) : Guid.Empty;
}

You’ll notice that we’re using another invented extension method here, IsRegexMatch, to determine if the Guid can be parsed, before returning a result. Here we’re really reducing the code we need to write, and potentially making the solution to it as natural as if it were part of the framework itself. This is a good place to be because we’re solving a real problem. So how much farther can we take this?

Level three: Using extension methods to embed business and validation logic

Now we’re talking. We can borrow some of those level two extension methods to create validations that are accessible from anywhere, but contain real value outside of providing convenience and clarity to our static code:

public static bool IsValidEmail(this string input)
{
    var email = “^[A-Z0-9._%-]+@[A-Z0-9.-]+.[A-Z]{2,4}$”;
    return input.IsRegexMatch(email, RegexOptions.IgnoreCase);
}

public static bool IsValidZipCode(this string input)
{
    var zip = “^[0-9]{5}(?:-[0-9]{4})?$”;
    return input.IsRegexMatch(zip);
}

public static bool IsValidShortZipCode(this string input)
{
    var zip = “^d{5}(?(?=-)-d{4})$”;
    return input.IsRegexMatch(zip);   
}

public static bool IsValidPostalCode(this string input)
{
    var canada = “^[ABCEGHJKLMNPRSTVXY]d[A-Z] d[A-Z]d$”;
    var uk = “^[A-Z]{1, 2}d[A-Zd]? d[ABD-HJLNP-UW-Z]{2}$”;
    return input.IsRegexMatchAny(canada, uk);              
}

We have addressed the need to create reusable, solution-agnostic code in the past in a very similar way using static classes. Extension methods augment that approach by providing a more natural way to use and organize snippets. I have found the third level of extension methods to be the most useful in the business layer, where, for example, you can use them on an entity base class to provide convenient caching methods in any project.

kick it on DotNetKicks.com

Technorati Tags: ,,
Kick It on DotNetKicks.com
blog comments powered by Disqus