Home > C#, delegates > Delegates are Immutable

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.

Categories: C#, delegates Tags: ,
  1. August 8, 2011 at 9:04 am

    Hi! Nice article, thanks.

    One note:
    > button.Click += (s, e) => ButtonClicked(s, e); // Exhibit B
    > Exhibit B wastes a little bit of garbage collected memory

    Actually it is not, as lambda is compiled to anonymous method, so it will waste stack memory, not heap’s one.

    • earwicker
      August 11, 2011 at 1:59 pm

      The lambda is turned into an anonymous method, but then at runtime we need an object to be allocated, so it can be added to the Click event. That object is a “delegate”, a compile-generated type that inherits System.Delegate. It stores the MethodInfo of the method to execute, and an object that will serve as the “this” reference when the method is called. So when you write a lambda and append it to an event (or assign it to a variable), you are allocating a GC-able object.

  2. Jai
    April 12, 2012 at 2:20 pm

    Why delegates are immutable?

    • earwicker
      May 27, 2012 at 11:35 am

      The same reason that strings are immutable. Consider the difference between a class and a struct. When you use assignment: a = b, with a class type, a now refers to the same object as b. But with a struct type, a remains a different object from b – assignment just makes a copy. Every variable of a struct type is bound to a unique object, whereas variables of class type don’t obey that rule.

      How can you tell the difference? By modifying (“mutating”) an object via one of the variables and then reading an object via the other variable, to see if you can observe the change you made. If you can, then the two variables refer to the same object. But if you make the class immutable, then it becomes impossible to detect how assignment works. This means that objects of your class can freely passed around within a process without you having to worry about making copies of them. It doesn’t make any difference whether they are copied or whether multiple variables refer to one shared object, and so we can take advantage of the efficiency of sharing one object instead of making many copies of it.

      If you want mutable delegates, that’s very easy to create just with a bit of indirection:

      Action realDelegate = Console.WriteLine;

      Action mutableDelegate = () => realDelegate();

      Now we can pass mutableDelegate to some other code and whenever we want to change its behaviour, we just assign a new delegate to realDelegate.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: