views:

117

answers:

3

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?

+1  A: 

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.

Hun1Ahpu
+3  A: 

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!

Damian Powell
+11  A: 

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.
Adam Robinson
I really like this answer! First point is so obvious, but something I did not think about. Second point I did not know but it is very nice :) Not sure I understand your third point. Thanks!
bobjink
@bobjink: Glad it's helpful! My third point was about being able to send data back to the object calling the event. Using the `EventArgs` approach, you can allow the consumer of your event to modify a property on it, like on the `CancelEventArgs` class, which will allow it to pass data back to you.
Adam Robinson
I see now! Thanks :)
bobjink