.NetAnti-patternsC#Extension methodsFrameworksSimple little things

Simple little things. The 42 extension method anti-pattern

Extension methods are a powerful feature in .NET, and have been increasingly adopted by the developer community. Also, LINQ and other framework features rely heavily on extension methods. In fact, extension methods were originally invented to be able to implement LINQ without having to change a lot of .NET framework base classes.

Basically, an extension method is implemented as a static method on a static class. For those of you who have never seen an extension method in real life, the first parameter in the signature of an extension method might look a bit peculiar.

public static bool Execute(this Task task, params Action[] actions)
{
    foreach (var action in actions)
    {
        action.Invoke();
 
        if (!ValidationManager.IsSucceeded)
        {
            ValidationManager.Handle();
            return false;
        }
    }
 
    return true;
}

This first parameter typed with the keyword this is just syntactic sugar to be able to tell for which type the extension method is valid. In this example it is valid for any instances of the type Task, and all of its subtypes.

Like using a regular method?

Using the extension method seems exactly like using a regular method on such instances, as in the example below.

public void RemoveTag()
{
    if (this.Execute(() => Tag.Remove()))
    {
        TaskManager.Run(this, select);
    }
}

But there’s a slight difference. Since the extension method is not implemented on the type itself, it is unable to reference the private parts of the type. Moreover, the extension method can only be called only on instances of the specific type, and not statically on the type itself.

Object extension methods

So far, so good. Now, as I said earlier, the developer community has warm embraced this cool .NET feature. In fact, when you browse for extension method on the internet, there’s tons of useful – and useless – extension methods available. If you look really careful, most of these are either defined as valid for object of string.

This is where the 42 anti-pattern comes from. Sometimes it’s hard to define the best location for an extension method. In the example of the Execute() methode above we couldn’t make up our minds – should it be valid for Task or for DomainObject?

In such case I can imagine that there will be developers out there who will now define the method on object, allowing the extension method to be applied from any arbitrary object in the application, clearly not what extension methods are intended.

public static bool Execute(this object task, params Action[] actions)

42

And this is where things are starting to dive of the deep end. Not only will you end up with a whole bunch of method that have nothing to do with most objects in your application, it can get even worse when you don’t have an object in your application that I can use to call the extension method on. What to do now?

Well, what if you would just define an arbitruary object hosting the extension method. Good idea dudes! We’ll just define the int instance 42 – just to have some fun and the method on 42.

if (42.Execute(() => Product.Remove()))
 {
     TaskManager.Run(this, select);
 }

Isn’t that sweet? I’m curious to see how long it takes until we find such code.

2 thoughts on “Simple little things. The 42 extension method anti-pattern

  1. It is as always: With great power comes great responsibility. The .NET Framework Design Guidelines specifically have a rule against defining extension methods on objects. I also try to prevent defining extension methods unless it makes the code really obvious; "42.Execute()" is clearly not. Because extension methods are static methods, they allow you to do "object t = null; t.Execute();" and depending on the extension method, it might work. Because of the way I’ve trained my brain however, this kind of code raises an PossibleNullReferenceExceptionDetected event in my brain, which makes me read and understand the code in a much slower pace. In many situations, I therefore prefer old-fashion static (task-specific) helper classes, instead of extension methods on everything, so: I wholeheartedly agree with you.

    1. You're so right. By the way, I still need to blog about the fact that you can raise extension methods on null objects. It's on my to-do list.

Comments are closed.