Playing with Extension Methods - also explaining why they are dangerous!

With great power comes great responsibility! The Spider-Man quote holds true for a lot of situations and Extension methods are not exempt! Below is some toying with extension methods to show the difference between good, useful extensions and crazy "should not do but am able to do"-extensions! Also let's have a look at the brilliant System. delegates Action<T>, Func<T, TResult>, Predicate<T> (which is little more than a special case of Func<T, bool>) and their siblings.

Update: Code attached at the bottom

Background

This is the second time I've used this Spider-Man quote in my blog for coding related matters! The other time was in my post about Dynamic Data (Presented MVC and Dynamic Data at the MSDN Summer Camp today (Slides, Demos etc.)).

Recently I was part of organizing an Open Space event in Stockholm. One of the topics we ended up talking about was "Extension methods or inheritance". It was a very interesting topic where a lot got said about extension methods; The good and the bad, the useful and the dangers! I remembered at the time that I had this toy test class lying about in our code repository where I'd played a bit with and pondered about extension methods. Now for about a week this post has been a draft in my Live Writer because yet again I've wanted to do more and more in a post before I post. Time to take it out there on my blog!

Initial samples

I don't really have to write out my Person class that I use below for you to understand upcoming code fragments. But here it is anyway:

public class Person
{
    public string Name { get; set; }
    public Person Mother { get; set; }
    public Person Father { get; set; }
}

There is a fine line between useful-and-readable-extensions and crazy-and-unnecessary-extensions. I am not going to try to influence you (much) in this post on where you should draw the line between the two. I will show some extensions that I consider to be on one end of the spectrum and I will show some that I consider to be on the other. You decide where your line is.

Note: Extension Methods are very useful in creating Fluent Interfaces coined by Eric Evans and Martin Fowler. Rhino Mocks is a great example of an application of the fluent interface paradigm.

Here are a few scenarios where extension methods have served me very well:

  • Replacing an override of .ToString() with an extension .Print() method. The rationale is that you can contain your .Print() method in a UI layer where you might need it rather than burdening your lower level (framework) objects with an overload that might not make sense down in the layer where the object is located.
  • .ForEach() Glenn Block first posted (as far as I know) the brilliant .ForEach() method, ForEach, a simple but very useful extension method, that extends IEnumerable<T> (any modern generically typed collection). I'm sure many of you have used this one already?
public static void ForEach<T>(this IEnumerable<T> list, Action<T> action)
{
    foreach (var t in list)
        action(t);
}

This would replace the foreach statement (assuming the existance of IEnumerable<Person> people):

foreach (var person in people)
{
    person.Name += " " + lastName;
}

Using the .ForEach() extension instead you get:

people.ForEach(p => p.Name += " " + lastName);

In this simple case using the extension actually simplifies the human readability of the code. In truth, though I've not tested, it seems likely that computers would take more effort to execute the latter statement. I mean think about it; extension methods are syntactic sugar only. We write the extension in our code but the compiler turns it into a method call to our static method ForEach<T>(this IEnumerable<T> list, Action<T> action). The exiting thing, I think personally, about extension methods is that you can attach them to any type, be it an object, interface or be it an enumeration. The above .ForEach() extension is attached to a generic Interface!

Suddenly you realize, when using the method signature above that you are doing nothing more than replacing a block of "as predictable code as possible" with a method call. Is it for the better? Sometimes yes, some times no! The lambda expression that takes the place of the Action<T> parameter has to be readable enough for this to be useful. Lambdas in general, when you're new to them, seem foreign to most but soon become as easy to read as other code. Our discussions at the Open Space in Stockholm led me to ask the question: Isn't it true that the classic foreach statement (not the extension) is as readable to a programmer as English is to Americans or Britons (or Swedish to Swedes in my case)? When I read the following

foreach (var something in somethings) { doSomething(); }

I read it very much like the extension method writes it out but inside my head:

somethings.ForEach(something => doSomething());

"There are somethings and forech something of those do something!"

The C# language is a language my fellow C# coders an I master. We have learned to read it quite fluently. The same as I would read plane English. For me the .ForEach() method feels nice to read and write and I think it's because it's mentally very close to the original non-extension method counterpart. What do you think?

However this only works as long as you KISS!

Here is a not so intuitive sample of another extension .ForEachIf<T>(this IEnumerable<T> list, Predicate<T> predicate, Action<T> action) where the lambda parameters, stacked on top of each other, are becoming a bother:

people.ForEachIf(p => p.Age <= legalAge, p => p.Father = new Person { Name = "Magnus" });

Readable? Yeaa... well kind' a but not really!

Problem: What the hell does .ForEachIf() do? Well obviously if the condition holds true then perform the action... right?

public static void ForEachIf<T>(this IEnumerable<T> list, Predicate<T> predicate, Action<T> action)
{
    foreach (var t in list)
        if (predicate(t))
            action(t);
}

That's exactly right!

But did this simplify things or make them harder to understand? I guess most of you would agree that this is not a simplification!

So we've found that it is possible to use an extension like .ForEachIf() to replace a rather common coding construct. But should you? I can take this example to the extremes with .TryCatchFinally() or .While() or just about any other block of C# code. Actually .While() is easy:

public static void While<T>(this T t, Predicate<T> predicate, Action<T> action)
{
    while (predicate(t))
        action(t);
}

But, come on, let's all agree that this is not the intent of extension methods!? Just because you can do something with an extension method doesn't mean you should!

The useful and powerful System delegates

Instead I want to write a bit about the System delegates Action<T>, Func<T, TResult> and Predicate<T>.

Action<T> is a method to run on any parameter of type T. There are in fact four System overloads; Action<T>, Action<T1, T2>, Action<T1, T2, T3> and Action<T1, T2, T3, T4>. Methods taking up to four parameters. There is also an Action delegate (System.Action) that takes no parameters.

Func is a function of T where the result is of type TResult. Also extremely useful! Func has equally many overloads as do Action<T> including the parameterless Func<TResult> that only returns the type TResult.

System.Predicate<T> is a special case of Func<T, TResult> where TResult is boolean. Predicate<T> is used in many places in the .NET framework. For instance in comparing actions like List<T>.Find(Predicate<T>) that returns a subset of the list where all items satisfy the predicate function.

Note: Another equally useful, in this case base class, is the Comparer<T> abstract class that implements IComparer<T> with the .Compare(T t1, T t2) method. But this has to do with generic collections: System.Collections.Generic Namespace ().

Once you get used to the Action, Func and Predicate you'll find yourself starting to use them often! And with lambda expressions as parameters things soon start to look real slick in your code! But now you have to be very wary of misuse!

One thing you can do with these delegates is call them recursively. In fact any function of action might be useful to do recursively:

public static T Recurse<T>(this Func<T, T> func, Predicate<T> condition, T t)
{
    return condition(t) ? func.Recurse(condition, func(t)) : t;
}

public static void Recurse<T>(this Action<T> action, Predicate<T> condition, T t)
{
    if (condition(t))
    {
        action(t);
        action.Recurse(condition, t);
    }
}

So now I can do this:

[Test]
public void RecurseAnyFunction()
{
    Func<int, int> addSomeValue = i => i + 5;

    var result = addSomeValue.Recurse(i => i < 100, 42);

    Assert.AreEqual(102, result);
}

The issue with this code is that it is not as easy anymore to write it. Nor very easy to read. The .Recurse() on action is worse than the .Recurse() on func I think. I sort of like the function version; "Call the function addSomeValue recursively while the result is less than 100. Start with the value 42. It is a bother that the initial parameter value comes at the end. Maybe this is what makes it a bit more strange to us: It is not close to how normal language and normal code works.

Let's do some really whacked out things with extensions!

As if the notion of replacing blocks of C# code with extensions or calling methods recursively wasn't enough. Take a look at this!

Note: So am I beginning to waste your time here? I hope not and I promise I have more useful things below! This section however is really wacky! I believe you need to see bad things too in order to make the right decisions no to use them yourself.

What's up with the delegates anyway? What if I am not interested in all those pesky parameters to my Action<T> delegates? Let's say you have an action method but not really a parameter to give it. Easy peasy piece of code; just write a delegate that ignores the parameter:

public static void Relax<T>(this Action<T> action)
{
    action(default(T));
}

Now I can write something crazy like this:

[Test]
[ExpectedException(typeof(NullReferenceException))]
public void RelaxedAction()
{
    Action<Person> actionOnPerson = p => p.Age = 42;
    actionOnPerson.Relax();
}

I create a delegate method that takes a Person parameter. Then I call it through my relax extension that will send in default(T) for the parameter. I don't have an instance of Person to give my method so I ignore the parameter. The default for a reference type is null. This will naturally cause the delegate to blow up because I didn't have any null checking code in my lambda. Man that was really pointless! ;~) The sample does, however point to the critical message in bold above; Just because you can do something with an extension method doesn't mean you should!

The opposite is more useful but still very pointless. For lack of a better name I tried to do the opposite of Relax; I came up with .Tighten() - though I guess LooseyGoosey would have been a really good name too:

public static Action<T1, T2> Tighten<T1, T2>(this Action<T1> action)
{
    return ((t1, t2) => action(t1));
}

With this method I can recast an Action<T> to an Action<T, T2>. It's doable just not reasonable:

[Test]
public void TightenedAction()
{
    Action<Person> actionOnPerson = p => p.Age = 42;

    Action<Person, long> action = actionOnPerson.Tighten<Person, long>();

    Person person = new Person();

    action(person, 47);

    Assert.AreEqual(42, person.Age);
}

That's really pointless too! I send in a parameter that I will never use.

Is there something good we can take away from these crazy samples? Yes I believe there is: Extensions can easily lets us reshape parameters to delegates! Here is something useful we can do armed with that insight:

Almost relaxed delegates in C# with extension methods

Are we talking about the invoke of events? There is a way to omit the parameters to event invocations! Sort of...

public static void RelaxedObject<T>(this EventHandler<T> eventHandler, T args) where T: EventArgs
{
    eventHandler.Invoke(null, args);
}

public static void RelaxedArgs(this EventHandler eventHandler, object o)
{
    eventHandler.Invoke(o, EventArgs.Empty);
}

public static void TotallyRelaxeded(this EventHandler eventHandler)
{
    eventHandler.Invoke(null, EventArgs.Empty);
}

Was that useful? No, not really. That was completely pointless. I can write that call myself.

Note: What's with the generic parameter to .RelaxedObject()? EventHandler<T> is the better generic version of the old pre generic EventHandler . It's not really where you call the event that you want to be relaxed; it's where you subscribe to it that you do.

But lets take this the other direction and see something interesting! I can simulate VisualBasic's relaxed delegates using extension methods on Action and Action<T> where T : EventArgs. Look at these extensions:

public static EventHandler<T> RelaxObject<T>(this Action<T> @delegate) where T : EventArgs
{
    return (o, e) => { @delegate(e); };
}

public static EventHandler RelaxEventArgs(this Action<object> @delegate)
{
    return (o, e) => { @delegate(o); };
}

public static EventHandler<U> RelaxData<T, U>(this Action<T> actionOnData, Func<U, T> extractData) where U : EventArgs
{
    return (o, e) =>
    {
        T data = extractData(e);
        actionOnData(data);
    };
}

public static EventHandler RelaxCompletely(this Action @delegate)
{
    return (o, e) => { @delegate(); };
}

public static EventHandler<T> RelaxCompletely<T>(this Action @delegate) where T: EventArgs
{
    return (o, e) => { @delegate(); };
}

This makes assigning delegate methods events a lot more appealing and almost relaxed delegates like!

Below I am also using a generic EventArg version that I consider almost standard today:

public class DataEventArgs<T> : EventArgs
{
    public DataEventArgs()
    {
        Data = default(T);
    }

    public DataEventArgs(T t)
    {
        Data = t;
    }

    public T Data { get; private set; }
}

For testing purposes I also use the following test classes:

private class testFoo
{
    public testFoo()
    {
        Called = false;
    }

    public bool Called { get; private set; }

    public void AMethod()
    {
        Called = true;
    }
}

private class EventRaiser
{
    public event EventHandler<DataEventArgs<string>> GenericEventWithDataEventArgs;
    public event EventHandler RegularOldFashionedEvent;

    public void InvokeGenericEventWithDataEventArgs(DataEventArgs<string> args)
    {
        GenericEventWithDataEventArgs.Invoke(this, args);
    }

    public void InvokeRegularOldFashionedEvent(EventArgs args)
    {
        RegularOldFashionedEvent.Invoke(this, args);
    }
}

The following tests exercises the extensions that allow a delegate with an incomplete signature to be assigned to the event handler:

[Test]
public void RelaxedDelegateThatIgnoresObject()
{
    string data = null;
    EventRaiser eventRaiser = new EventRaiser();

    Action<DataEventArgs<string>> relaxedDelegate = e => { data = e.Data; };

    eventRaiser.GenericEventWithDataEventArgs += relaxedDelegate.RelaxObject();

    eventRaiser.InvokeGenericEventWithDataEventArgs(new DataEventArgs<string>("The Message!"));

    Assert.AreEqual("The Message!", data);
}

[Test]
public void RelaxedDelegateThatIgnoresEventArgs()
{
    object sender = null;
    EventRaiser eventRaiser = new EventRaiser();

    Action<object> relaxedDelegate = o => { sender = o; };

    eventRaiser.RegularOldFashionedEvent += relaxedDelegate.RelaxEventArgs();

    eventRaiser.InvokeRegularOldFashionedEvent(EventArgs.Empty);

    Assert.AreSame(eventRaiser, sender);
}

[Test]
public void RelaxedDelegateThatIgnoresBoth()
{
    bool notified = false;
    EventRaiser eventRaiser = new EventRaiser();

    Action eventHandler = () => { notified = true; };

    eventRaiser.RegularOldFashionedEvent += eventHandler.RelaxCompletely();

    eventRaiser.InvokeRegularOldFashionedEvent(EventArgs.Empty);

    Assert.IsTrue(notified);
}

[Test]
public void RelaxedDelegateThatIgnoresBothGeneric()
{
    bool notified = false
    EventRaiser eventRaiser = new EventRaiser();

    Action<DataEventArgs<string>> eventHandler = e => { notified = true; };

    eventRaiser.GenericEventWithDataEventArgs += eventHandler.RelaxObject();

    eventRaiser.InvokeGenericEventWithDataEventArgs(new DataEventArgs<string>(string.Empty));

    Assert.IsTrue(notified);
}

[Test]
public void RelaxedHandlerMethod()
{
    testFoo foo = new testFoo();
    EventRaiser eventRaiser = new EventRaiser();

    eventRaiser.RegularOldFashionedEvent += ((Action)(foo.AMethod)).RelaxCompletely();

    eventRaiser.InvokeRegularOldFashionedEvent(EventArgs.Empty);

    Assert.IsTrue(foo.Called);
}

[Test]
public void RelaxedHandlerMethodsOnGenericEvent()
{
    testFoo foo = new testFoo();
    EventRaiser eventRaiser = new EventRaiser();

    eventRaiser.GenericEventWithDataEventArgs += ((Action)(foo.AMethod)).RelaxCompletely<DataEventArgs<string>>();

    eventRaiser.InvokeGenericEventWithDataEventArgs(null);

    Assert.IsTrue(foo.Called);
}

[Test]
public void RelaxedDelegateThatIgnoresObjectAndExtractsData()
{
    string data = null;
    EventRaiser eventRaiser = new EventRaiser();

    Action<string> relaxedDelegate = s => { data = s; };

    eventRaiser.GenericEventWithDataEventArgs += relaxedDelegate.RelaxData((DataEventArgs<string> d) => d.Data);

    eventRaiser.InvokeGenericEventWithDataEventArgs(new DataEventArgs<string>("The Message!"));

    Assert.AreEqual("The Message!", data);
}

In some cases above the resulting code was, in my opinion not improved by calling an extension method to do a simple thing.

This:

Action eventHandler = () => { notified = true; };

eventRaiser.RegularOldFashionedEvent += eventHandler.RelaxCompletely();

can be as clearly expressed using this statement:

eventRaiser.RegularOldFashionedEvent += (o, e) => { notified = true; };

I like the event handling extensions since I do not like the fact that C# does not offer relaxed delegates!

Conclusion

Our conclusion from the Open Space event was that extensions are supposed to be for powerful, easy to use, cases where it makes sense to go to an extension rather than just extracting a method or creating another utility class. Do you agree?

Cheers,

M.

P.S: Walking a tree using extensions might look something like this:

public static void DepthFirst<T>(this ITree<T> root, Action<ITree<T>> action)
    where T : class
{
    if (root.Left != null)
        root.Left.DepthFirst(action);
    if (root.Right != null)
        root.Right.DepthFirst(action);

    action(root);
}

public static ITree<T> FindLeaf<T>(this ITree<T> root, Predicate<T> predicate)
    where T : class
{
    if (predicate(root.Node))
        return root;

    ITree<T> tree = null;

    if (root.Left != null)
        tree = root.Left.FindLeaf(predicate);

    if (tree == null && root.Right != null)
        tree = root.Right.FindLeaf(predicate);

    return tree;
}

Technorati Tags:

DotNetKicks Image

posted @ Thursday, October 02, 2008 1:51 AM

Print

Comments on this entry:

No comments posted yet.

Your comment:



 (will not be displayed)


 
 
 
Please add 5 and 7 and type the answer here:
 

Live Comment Preview: