22

The documentation for EventHandler<TEventArgs> says:

The second parameter is a type derived from EventArgs and supplies any fields or properties needed to hold the event data.

and it seems to be generally recommended throughout the .Net documentation.

However it turns out that I can do the following which works just fine:

public event EventHandler<int> Panned;

and invoke the event handler as:

int value = 10;
if (Panned != null)
{
    Panned(this, value);
}

and on the observer side:

subject.Panned += (sender, e) =>
{
    Console.WriteLine(e);
};

To me this seems better than littering the code with little classes that inherit from EventArgs or having a generic EventArgs as proposed by Does .NET have a built-in EventArgs<T>?

So why is it required that I inherit the EventHandler generic argument from EventArgs?

Community
  • 1
  • 1
Kostub Deshmukh
  • 2,852
  • 2
  • 25
  • 35
  • 10
    If you use .NET 4.0 or older `public event EventHandler Panned;` would fail. [In 4.0](https://msdn.microsoft.com/en-us/library/db0etb8x(v=vs.100).aspx) it had a `where TEventArgs : EventArgs` constraint on it. That constraint was dropped [in 4.5](https://msdn.microsoft.com/en-us/library/db0etb8x(v=vs.110).aspx) – Scott Chamberlain Aug 04 '15 at 19:26
  • 1
    Oh interesting. That's good to know. – Kostub Deshmukh Aug 04 '15 at 19:32

2 Answers2

24

If all you need to do is pass an int to the handler then what you are doing is fine.

It used to be the case (before .NET 4.5) that the EventHandler type argument TEventArgs was constrained to inherit from EventArgs but not anymore:

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

The fact that MS dropped the constraint should tell you that they were being too strict and what you are doing is fine.

In the case that you need to pass a complex type to the handler then you might aswell inherit EventArgs for reasons of polymorphism. Also, the EventArgs.Empty member is useful.

Alex Booker
  • 10,487
  • 1
  • 24
  • 34
  • Also, if your event doesn't wire up with a derivative of the default `EventHandler` delegate, powershell (at least up to 4) will fail miserably and crash if you try to set a handler using `Register-ObjectEvent` – Eris Aug 04 '15 at 19:42
7

This is just a convention. In fact, you don't even have to use the EventHandler<> generic delegate. You could have:

public event Action SomeEvent;

public void OnAction()
{
    var a = this.SomeEvent;
    if (a != null)
    {
        a();
    }
}

Of course, the convention is there for a reason. As far as I know, every standard .NET event follows the pattern of using a void-returning delegate that takes an object parameter and a second parameter of EventArgs or a derived type. This makes it easy to use these events without having to refer to the documentation each time.

Silly, Silly, Silly!

Will this work?...

class Program
{
    public static event Func<int> SomeEvent;

    static void Main(string[] args)
    {
        SomeEvent += () => 7;
        SomeEvent += () => 8;
        var a = SomeEvent();
        Console.WriteLine(a);
    }
}

I tried it: it does! Of course, it's very odd to have an event where the delegate has a return value, because it's not obvious which handler's value will be returned to the caller if there are multiple attached handlers. In the above example, it turns out that 8 is written to the console.

Interesting but, I suspect, useless ;-)

Would You Ever Use This?

I don't think it would ever be sensible to have a non-void returning delegate type, as in my example. However, you might consider using a delegate whose parameters are value types (structs, not classes) for performance reasons. It might be possible to use events without incurring the garbage collection penalty of allocating EventArgs objects on the heap.

Olly
  • 5,966
  • 31
  • 60