Helpful Extension Methods

2013-08-25 1:32 AM

Having done a lot of writing in C# in the past several years, I've wound up with a fairly extensive personal library of helpful extension methods.

Introduction

In case you're unfamiliar, Extension Methods are a compiler trick that allow you to seemingly add functionality to existing types by creating static methods within a static class, and specifying the target type with the this keyword. For example, here's an extension method to determine if an int is even:

public static class Extensions
{
    public static bool IsEven(this int num)
    {
        return num % 2 == 0;
    }
}

Take note of the construction: - Class: the extension method is within a public, static class. Note that one extension-method-containing class may contain any number of extension methods. - Method: like the class, the method is public and static. - Type: the int argument is enhanced with the this keyword, which signals to the C# compiler that this is an extension method. Note that in a method with multiple arguments, the extended type must be the first one (this always comes first).

Past the special treatment of adding the this keyword to the first argument, the method is constructed in a perfectly normal way.

Calling the method is trivially simple:

var myInt = 2;
Console.WriteLine(myInt.IsEven());
myInt = 3;
Console.WriteLine(myInt.IsEven());

The first output will be "true," while the second one will be "false." Note that you are given the ability to call IsEven() as if it were an instance method of int.

As I mentioned before, this is a compiler trick: you're not actually modifying the int type in any way; you've just exposed a public method that the C# compiler turns into a faux-instance method which is callable as if it were part of the type. To demonstrate this, you can also call the method directly, in the following fashion:

var myInt = 2;
Console.WriteLine(Extensions.IsEven(myInt));
myInt = 3;
Console.WriteLine(Extensions.IsEven(myInt));

These two bits of code do the exact same thing.

Multiple Arguments

Additionally, adding more arguments to the method is very easy:

public static class Extensions
{
    public static bool IsDivisibleBy(this int num, int divisor)
    {
        return num % divisor == 0;
    }
}

And the following code will produce the same output as it did before:

var myInt = 2;
Console.WriteLine(myInt.IsDivisibleBy(2));
myInt = 3;
Console.WriteLine(myInt.IsDivisibleBy(2));

Benefits

Extension methods offer a number of benefits - they allow you to seemingly extend functionality for types which are out of your control, which is great; however, there are two points that make them particularly useful.

Any type

One huge benefit extension methods grant is the ability to add functionality to any type: you are not restricted to classes. This means that you can define an interface, then "add functionality" to it by creating extension methods.

This is most readily apparent in the Linq library, where the query operators extend functionality for IEnumerable<T> types. The declaration of the Where<T> operator looks like this

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

and allows you to call it on any IEnumerable<T> - even though the `IEnumerable interface only contains functionality for iterating over a series of objects.

I have taken advantage of this several times by defining an interface with no requirements, and then defining a few extension methods that add behavior to that interface. This has the effect of allowing a developer to add that functionality to their new class by simply declaring that it implements a particular interface.

Null arguments

While some might consider it annoying that extension methods don't work as actual instance methods (for instance, you do not have access to any non-public members of the target type), I have used this fact to my advantage many times, particularly in null-checking arguments.

For instance, consider the ubiquitous .ToString() method in C# which is a member of the base object type: the class which every reference type extends: this method is very useful for turning objects into strings; however, it starts failing if you are uncertain of whether or not your object is null.

string GetString(object myObject)
{
    return myObject.ToString();
}

This method will obviously blow up if you pass in null, and that's probably not the desired behavior. So, we could introduce a trivial change to null-armor it"

string GetString(object myObject)
{
    if (myObject == null)
    {
        return null;
    }
    return myObject.ToString();
}

That's all well and good: it will work. However, write that null check a couple of hundred times over the course of a few months and you'll quickly grow tired of those extra lines cluttering up your code. We can solve this with an extension method! And this brings me to my first Helpful Extension Method: NullableToString

NullableToString

public static class Extensions
{
    public static string NullableToString(this object obj)
    {
        if (obj == null)
        {
            return null;
        }
        return obj.ToString();
    }        
}

This functionality is still very simple; but suddenly it unlocks the ToString-ing of objects without caring whether or not they're set to an instance of an object! We can now seemingly call an instance method of the incoming object with complete immunity from null issues!

string GetString(object myObject)
{
    return myObject.NullableToString(); // Don't even care if it's null or not!
}

There is one more step to take here though: sometimes I expect an actual instance of a string to come out of the method, whether or not it's actually populated with anything. This typically occurs when I'm following the ToString() with a ToUpperInvariant() or some other method that expects an instance of a string to be passed in.

NullableToEmptyString

public static class Extensions
{
    public static string NullableToEmptyString(this object obj)
    {
        if (obj == null)
        {
            return string.Empty;
        }
        return obj.ToString();
    }        
}

We can now do whatever we want with the result with complete safety.

string GetString(object myObject)
{
    return myObject.NullableToEmptyString().ToUpperInvariant();
}