Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Events convention - I don't get it

My class with an event:

public class WindowModel
{
    public delegate void WindowChangedHandler(object source, WindowTypeEventArgs e);
    public event WindowChangedHandler WindowChanged;  

    public void GotoWindow(WindowType windowType)
    {
        this.currentWindow = windowType;
        this.WindowChanged.Invoke(this, new WindowTypeEventArgs(windowType));
    }
}

Derived event class:

public class WindowTypeEventArgs : EventArgs
{
    public readonly WindowType windowType;

    public WindowTypeEventArgs(WindowType windowType)
    {
        this.windowType = windowType;
    }
}

Some other class that register it to the event:

private void SetupEvents()
{
   this.WindowModel.WindowChanged += this.ChangeWindow;
}

private void ChangeWindow(object sender, WindowTypeEventArgs e)
{
   //change window
}

What have I gained from following the .Net convention? It would make more sense to have a contract like this

public delegate void WindowChangedHandler(WindowType windowType);
public event WindowChangedHandler WindowChanged;

Doing it this way, I don't need to create a new class and is easier to understand. I am not coding a .Net library. This code is only going to be used in this project. I like conventions but am I right when I say that in this example it does not make sense or have i missunderstood something?

like image 669
Mads Andersen Avatar asked Apr 19 '10 17:04

Mads Andersen


3 Answers

Viewed in isolation, yes, you're correct: the .NET conventional syntax is more verbose and less intuitive, but there are advantages:

  • Future changes to the information passed by your event do not automatically require changes to every consumer of the event. For example, if you wanted to add an additional piece of information to your event--say, a WindowTitle string--you'll have to modify the signature of every single function that gets attached to that event, regardless of whether or not they use it. With the EventArgs approach, you add the property to the arguments and only alter the functions that need to take advantage to the additional information.
  • Since .NET 2.0 introduced the EventHandler<TEventArgs> delegate type, you no longer need to define your own event delegates manually. In your example, you would type your event as EventHandler<WindowTypeEventArgs> instead of WindowChangedHandler.
  • The EventArgs approach makes it easy to pass multiple types of information back to the calling function. If you needed to do this in your alternative example (that passes the event parameter directly), you'd still end up creating your own -tuple class to hold the information.

The impact of the first one is made more evident when you look at the actual pattern for .NET events in creating a protected virtual function that actually does the invoking. For example:

public event EventHandler<WindowTypeEventArgs> WindowChanged;

protected virtual void OnWindowChanged(WindowTypeEventArgs e)
{
    var evt = WindowChanged;

    if(evt != null) evt(this, e);
}

There are a couple of things I'd like to point out here:

  1. Using the pattern of creating this event-invoking method allows you to avoid null checking throughout your code (an event without any functions attached to it will be null and will throw an exception if you try to invoke it)
  2. This pattern also allows classes that inherit from you to control the order of invocation, allowing them to execute their code explicitly either before or after any outside consumers
  3. This is especially important in multithreaded environments. If you just said if(WindowChanged != null) WindowChanged(this, e);, you would actually run the risk of the WindowChanged event becoming null between the time you check it and the time you call it. This isn't important to do in single-threaded scenarios, but is a great defensive habit to form.
like image 73
Adam Robinson Avatar answered Sep 27 '22 23:09

Adam Robinson


I recognise your confusion! I had the same feeling when I first looked at this too.

The big thing to realise is that it doesn't give you much of an advantage programmatically speaking, but it is a convention that is well known in the framework. As such, there are plenty of tools that expect the void EventName(object sender, EventArgs e) signature. Some IoC containers, for example, can use this signature to auto wire events at construction time.

In short, it looks a bit weird, but it's a convention. Stick with it and the bulb will light up eventually!

like image 31
Damian Powell Avatar answered Sep 28 '22 00:09

Damian Powell


You can use your delegate. Nobody will force you. It's just a good pattern for events.

If you use standart Sender-EventArgs pattern you'll be able to use the same ChangeWindow handler for other events too.

like image 37
Hun1Ahpu Avatar answered Sep 28 '22 01:09

Hun1Ahpu