C# Delegates and Events


Readers of this blog will know that I’m in the process of learning the ins and out of C#. Coming from a Java/C++ background I was have a bit of trouble getting to grips with C# Delegates and Events. None of the examples I looked at really made sense to me. Isn’t this pretty much the same as Interfaces?

I guess the correct answer is both yes and no. I’m pretty sure you can achieve similar results using plain Interfaces, but now after playing around with Delegates and Events, I kind of see where using them might seem like a better approach.

So, how to get started? Let’s look at a simple example. You have a class that you want to enable to call back it’s caller when something happens – an event, in other words. You have a vague idea of what type of information you want to send to the caller when the event occurs. This is where the delegate steps in and should be your first stop:

    public delegate void MyDelegate1(object sender, EventArgs e);

The delegate is usually declared at the same “level” you would an interface or class. The parameters I have used seem to be the C# norm, but based on my experience playing around, this is not necessary. You can send no parameters or more parameters and they can be of any type you want, if any. The return type can also be something other than void, but as we will see below, this makes little sense for most common scenarios. For now, the important thing to notice is the delegate keyword.

So, how to use it? You need to declare an event:

public class Worker
{
    public event MyDelegate1 MyEvent1;

    public void DoIt()
    {
        if (MyEvent1 != null)
        {
            EventArgs e = new EventArgs();
            MyEvent1(this, e);
        }
    }
}

By creating an instance of the Worker class and calling the DoIt() method we trigger the event. Well, nearly. We still need to register an event handler, but more on that in a moment. For now, notice the event’s declaration. Notice the relationship between our event and delegate. Also notice also the signature of the call to MyEvent1() in the method body. It must be identical to the delegate method signature and return type. Anything else will cause a compiler error. You will also notice that we check if the event is null. A NullReferenceException will be thrown if MyEvent1 is null, and we don’t want that. Not all events will have registered handlers.

The code compiles, but nothing happens. We need to register an event handler. Let’s register two of them just to make the point:

public class Runner
{
    public void SomeMethod()
    {
        Worker w = new Worker();
        w.MyEvent1 += new MyDelegate1(RespondToEvent1Alt1);
        w.MyEvent1 += new MyDelegate1(RespondToEvent1Alt2);
        w.DoIt();
    }

    public void RespondToEvent1Alt1(object sender, EventArgs e)
    {
        Console.WriteLine("Responding to event 1, alternative 1");
    }

    public void RespondToEvent1Alt2(object sender, EventArgs e)
    {
        Console.WriteLine("Responding to event 1, alternative 2");
    }
}

We are doing a few things here. We start by registering code handlers for our event. We are registering two methods to respond to the event. Both method handler’s signature must match the delegate signature. When the event is triggered in DoIt(), the methods will be called in the order they are registered.

What happens if we declare a new delegate with a non-void return type and different signature?

    public delegate int MyDelegate2();

This delegate wants any event handlers to return an int when done processing the event. Event handlers receive no parameters from the event. We modify the Worker class to look like this:

public class Worker
{
    public event MyDelegate1 MyEvent1;
    public event MyDelegate2 MyEvent2;

    public void DoIt()
    {
        if (MyEvent1 != null)
        {
            EventArgs e = new EventArgs();
            MyEvent1(this, e);
        }

        if (MyEvent2 != null)
        {
            int x = MyEvent2();
        }
    }
}

The important thing to notice is the return type and parameters of the call to the new delegate. What happens if we also register two handlers for this event?

public class Runner
{
    public void SomeMethod()
    {
        Worker w = new Worker();
        w.MyEvent1 += new MyDelegate1(RespondToEvent1Alt1);
        w.MyEvent1 += new MyDelegate1(RespondToEvent1Alt2);
        w.MyEvent2 += new MyDelegate2(RespondToEvent2Alt1);
        w.MyEvent2 += new MyDelegate2(RespondToEvent2Alt2);
        w.DoIt();
    }

    // skipped RespondToEvent1Alt1() and RespondToEvent1Alt2() here - same as above

    public int RespondToEvent2Alt1()
    {
        Console.WriteLine("Responding to event 2, alternative 1");
        return 24;
    }

    public int RespondToEvent2Alt2()
    {
        Console.WriteLine("Responding to event 2, alternative 2");
        return 42;
    }
}

When the new event triggers in DoIt() it calls the two new event handlers in order as expected. However, the value of the local variable x will be the value of the last event handler called, here that’s 42. Returning a value from a delegate doesn’t make much sense. There is no way of telling how many event handlers will be registered, if any – which is kind of the point.

Maybe there is a way to process the return values after each event handler, but I haven’t seen this mentioned in any of the C# documenation I’ve seen – not that I’ve been looking actively 😉

Advertisements

2 thoughts on “C# Delegates and Events

  1. Great post Lee!

    As far as I can remember, I think I have never used anything other than void in my delegates. I did a quick search in the solution I currently have open in Visual Studio and found approx 90 delegates, but 88 with void return. In the two cases I found a return type, it looked more like a misunderstanding of what a delegate should be used for than and that they should rather have used an interface (as your described).

    One small tip:
    w.MyEvent1 += new MyDelegate1(RespondToEvent1Alt1);

    can be written:
    w.MyEvent1 +=RespondToEvent1Alt1;

    (or something like that)

    Cheers!

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s