I have moved!

I've moved my blog
CLICK HERE
Showing posts with label delegates. Show all posts
Showing posts with label delegates. Show all posts

Tuesday, 3 February 2009

Further Muddying the ForEach Waters

ForEach is regularly proposed as a missing feature in the BCL but has been rejected in the past apparently because it wouldn't be "functional" enough, doesn't return anything so calls cannot be chained together in a LINQ-like pipeline, and so on. So here's a different approach that is undeniably functional, and does support chaining together.

public static IEnumerable<Action> ForEach<T>(this IEnumerable<T> source, 
                                             Action<T> action)
{
    foreach (T item in source)
    {
        T captured = item;
        yield return () => action(captured);
    }
}

That extension method turns a list of items of any type into a list of actions, by binding a dedicated action to each item. The resulting list can then be operated on further - e.g. interleaved with other lists, reversed, counted, and so on.

Another useful operation is a specialised form of Aggregate that turns a list of actions into a single action. The CLR already has an efficient representation of this: a multicast delegate, so that's what I prepare and return:

public static Action Aggregate(this IEnumerable<Action> actions)
{
    Action del = null;

    foreach (Action action in actions)
        del += action;

    return del;
}

So now I can write things like:

int[] arr = { 3, 1, 4 };

Action act = arr.ForEach(Console.WriteLine)
            .Concat(arr.ForEach(Console.WriteLine).Reverse())
            .Aggregate();

act();

(Oh, how much clearer that would look with operator overloading through extension methods, using + to concatenate the lists, but I digress...)

Which prints:

3
1
4
4
1
3

So the bound actions generated from the lists can be chained, reversed, rechained, signed in triplicate and finally buried for three months and then recycled as firelighters.

By the way, the feedback from MS on the plain ForEach suggestion is baffling in any case. They don't want to support stateful, imperative programming? Then they need to remove quite a lot of existing features from the language, such as assignment! By all means support functional programming smoothly with C# (a brilliant strategy) but don't get confused into thinking that you're maintaining F#. The whole point of C# is to be the number 1 language for building and using libraries in the CLR, and such libraries are expressed through components that are, for better or worse, brimming with mutable state.

Meanwhile, you can add features for specifying whether a given data structure is mutable or not, and this will take control of naughty mutation far more effectively than merely failing to add frequently useful features to the BCL.

Friday, 13 June 2008

The classic delegate/foreach interaction bug (and a solution)

If you've used C# anonymous delegates or lambdas for a while (or anonymous functions in JavaScript) you will have encountered this problem. You make a delegate for each item in a list, and they all seem to wind up referring to the last item on the list. FUME!

It's just an unfortunate side effect of the way foreach works. It reuses the same loop variable each time around the loop, and so that loop variable is created outside the scope of the loop, and so all the delegates you create inside the loop are actually looking at the same variable, not their own local copies.

This demonstrates the problem, and also gives a neat solution:

static class Extensions
{
 public static void ForEach<T>(this IEnumerable<T> on,
                               Action<T> action)
 {
     foreach (T item in on)
         action(item);
 }
}

class Program
{
 static void Main(string[] args)
 {
     // A list of actions to execute later
     List<Action> actions = new List<Action>();

     // Numbers 0 to 9
     IEnumerable<int> numbers = Enumerable.Range(0, 10);

     // Store an action that prints each number (WRONG!)
     foreach (int number in numbers)
         actions.Add(() => Console.WriteLine(number));

     // Run the actions, we actually print 10 copies of "9"
     foreach (Action action in actions)
         action();

     // So try again
     actions.Clear();

     // Store an action that prints each number (RIGHT!)
     numbers.ForEach(number =>
         actions.Add(() => Console.WriteLine(number)));

     // Run the actions
     foreach (Action action in actions)
         action();
 }
}

By using ForEach (an extension method) instead of using foreach directly, we make the problem disappear, because now each time around the loop we have a unique number variable which is properly captured by the delegates we created.

Notes:

  • The ForEach extension method I define above is actually already available as a method in the List<T> class, so if you're looping over a List<T>, you don't need that extension method.
  • That extension method ought to be added to the System.Linq.Enumerable class, oughtn't it?
  • One situation this can't help with is yield return. If you are looping through a list of items, and then you want to yield return each item, you're out of luck with this solution.

Sunday, 11 May 2008

Delegates are Immutable

Suppose you see this in some code:

button.Click += new EventHandler(button_Click);

Naturally you would conclude that Click is an event and button_Click is a method name (formally described as a method group, because the name by itself identifies one or more methods with different parameters). After the above line of code has been executed, the method button_Click will run each time the Click event fires (presumably when the button is clicked).

Also these days we don't need to be so verbose and can just write:

button.Click += button_Click;

Perfectly straightforward. But full of assumptions that may not be true. I deliberately chose the name button_Click to make you think it identified a method, because that's the kind of name the Visual Studio IDE gives to automatically generated event handler methods. What if only the identifier was different?

button.Click += ButtonClicked; // Exhibit A

Maybe ButtonClicked is in fact not a method but another event. In which case, what does Exhibit A actually do?

Another way of phrasing all this: how does it differ from this next line of code?

button.Click += (s, e) => ButtonClicked(s, e); // Exhibit B

If ButtonClicked is a method name, then there is no effective difference between Exhibit A and Exhibit B. Exhibit B wastes a little bit of garbage collected memory, but otherwise it sets up something with identical behaviour. Exhibit A creates a delegate that calls the method ButtonClicked directly, whereas Exhibit B builds an anonymous method that calls ButtonClicked and then wraps it in a delegate. The result is an unnecessary layer of wrapping. So if someone were maintaining the code and they came across Exhibit B, they could safely change it to Exhibit A.

But what if, as seems more likely, ButtonClicked is an event? It all depends on what state ButtonClicked is in at the time. Suppose that before Exhibit A executes, there are currently no handlers attached to either event. The debugger would tell us that both button.Click and ButtonClicked have the special value null.

Immediately after Exhibit A executes, the debugger would continue to tell us exactly the same thing: Exhibit A would have no effect on anything.

But what if ButtonClicked had previously been set up as follows?

ButtonClicked += (s, e) => Console.WriteLine(e.ToString());

Then Exhibit A would have an effect. It would mean that the anonymous function attached to ButtonClicked would also execute if the button is clicked.

And what if a second handler were attached to ButtonClicked after Exhibit A was run?

ButtonClicked += (s, e) => Console.WriteLine("And again");

This change would have no effect at all on the behaviour when the button is clicked.

The reason for this is that delegates are immutable - they cannot be modified after they are constructed - and so should be treated like value types. Exhibit A could be interpreted as an instruction to make button.Click point to the exact same delegate object as ButtonClicked. And in fact, C# and the runtime are probably at liberty to do it that way. But as there is no way to modify an existing delegate, therefore it makes no difference whether the two events point to one delegate or to two separate copies.

When we attach our second handler to ButtonClicked, this results in the creation of a completely new delegate object that "multi-casts" invocations on to both handlers. However, the previous delegate object is still attached to button.Click.

Why is this important? Because the sort of place where you might want to set up this "chaining" of events is in the constructor of a window that owns a button. The desired behaviour is that when the button is clicked, the window's ButtonClicked event should fire. But if you try to achieve this with Exhibit A, you will find that it does not work, because in the window's constructor, nothing has been attached to ButtonClicked yet. What you actually need is Exhibit B.

And yet if ButtonClicked was a method, there would be no noticeable difference between the two.

So the short answer to the question, "What's the difference between Exhibit A and Exhibit B?" is...

It depends.