18
\$\begingroup\$

There is as of .NET 2.0 a generic EventHandler delegate type, which can be used to define an event in terms of the EventArgs type you wish to use, without having to create a redundant delegate type to use in the event declaration.

Well, what about the EventArgs? I often find myself having to create one-offs derived from EventArgs to encapsulate extra data I wish to send. Why can't that be declared generically as well? If all I need to pass is a string, or an integer, or a Point, why do I have to create a StringEventArgs, IntEventArgs and/or PointEventArgs class respectively?

I roughed up two very basic EventArgs generic types as a proof-of-concept:

public class ReadOnlyEventArgs<T>:EventArgs
{
    public T Parameter { get; private set; }

    public ReadOnlyEventArgs(T input)
    {
        Parameter = input;
    }
}

public class EventArgs<T>:EventArgs
{
    public T Parameter { get; set; }

    public EventArgs(T input)
    {
        Parameter = input;
    }
}

The obvious difference is whether you want event handlers to be able to modify your arguments. Sometimes it's a good idea, most of the time not so good. Usage would simply look like:

public event EventHandler<EventArgs<string>> StringValidationRequested;
public event EventHandler<ReadOnlyEventArgs<int>> RecordSelectedForRetrieval;
public event EventHandler<ReadOnlyEventArgs<Point>> MouseLocationReported;

Now, obviously these are for simpler situations, but I've found most situations of event raising to be pretty simple. The only possible thing I'd add would be two more classes derived from CancelEventArgs to add that additional functionality. The fact that the Parameter can be anything including a class makes it flexible enough to pass complex data, and .NET 4's Tuples take care of the remaining situations where you might not want to define your own complex type. The only drawback I can think of is not being able to descriptively name your parameter; it's generic.

\$\endgroup\$
  • \$\begingroup\$ You've only now just found out about it? It's been there since .NET 2.0. ;) \$\endgroup\$ – Jeff Mercado Oct 19 '11 at 16:42
  • \$\begingroup\$ Well I didn't JUST find out about it but my steady experience with .NET started at 3.5. Editing. \$\endgroup\$ – KeithS Oct 19 '11 at 16:50
  • \$\begingroup\$ You know, I'm thinking in the case of the mutable event args with tuples, if you wanted to support that, it might be useful to add (extension) methods for the EventArgs used to create the new set of arguments. Something like ChangeItem1() and so on. Then that would just be another set of overloads. \$\endgroup\$ – Jeff Mercado Oct 19 '11 at 19:03
  • \$\begingroup\$ Along the same lines as this question, I find myself needing to wire into an event handler on a pop-up form that can pass an object back to the parent. ItemChangedEventHandler seems like it would fit my needs, but I do not have a way to set the Index value in the ItemChangedEventArgs object that I want to pass back to the parent (it is not public). \$\endgroup\$ – jp2code Jun 5 '15 at 20:03
8
\$\begingroup\$

You could make it a little bit more useful if you added some supporting factory methods to create the arguments. That way you don't have to use the constructor and its somewhat awkward syntax (having to supply the types).

public static class EventHandlerExtensions
{
    public static EventArgs<T> CreateArgs<T>(
        this EventHandler<EventArgs<T>> _,
        T argument)
    {
        return new EventArgs<T>(argument);
    }

    public static ReadOnlyEventArgs<T> CreateArgs<T>(
        this EventHandler<ReadOnlyEventArgs<T>> _,
        T argument)
    {
        return new ReadOnlyEventArgs<T>(argument);
    }
}

Then to create the instance of the arguments, you could just do this:

public event EventHandler<EventArgs<SomeArg>> SomeEvent;
public event EventHandler<ReadOnlyEventArgs<SomeArg>> SomeOtherEvent;

protected virtual void OnSomeEvent(SomeArg argument)
{
    var someEvent = SomeEvent;
    if (someEvent != null)
    {
        var args = someEvent.CreateArgs(argument);
        // instead of:
        //var args = new EventArgs<SomeArg>(argument);
        someEvent(this, args);
    }
}

protected virtual void OnSomeOtherEvent(SomeArg argument)
{
    var someOtherEvent = SomeOtherEvent;
    if (someOtherEvent != null)
    {
        var args = someOtherEvent.CreateArgs(argument);
        // instead of:
        //var args = new ReadOnlyEventArgs<SomeArg>(argument);
        someOtherEvent(this, args);
    }
}

It might be useful to have options for multiple parameters (similar to how the tuple is). Though it might not be feasible to have all combinations of multiple (im)mutable arguments.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Love it. As for multiple parameters, yes I could create a separate class for up to 16 generic types like Tuple/Func/Action, but first, it really wouldn't be that much more elegant versus just having you specify a Tuple, and second, just for EventArgs, ReadOnlyEventArgs, CancelEventArgs and ReadOnlyCancelEventArgs, that's 64 class definitions to write, test and sort through in IntelliSense. I think just making you specify a Tuple and access the args with e.Parameter.Value1 etc is saner, through it still requires a generic constructor. \$\endgroup\$ – KeithS Oct 19 '11 at 17:49
  • \$\begingroup\$ Ah, I thought you get the Handled/Cancel property in the base EventHandler. I guess that's a WinForms feature. Having a factory method to handle multiple arguments and pack them into a tuple could make that scenario much more cleaner. Though it might be weird to specifically support a mutable EvenArgs<Tuple> as the tuple itself is immutable. It might be ok to omit those overloads. \$\endgroup\$ – Jeff Mercado Oct 19 '11 at 18:06
  • \$\begingroup\$ Yes, the (non-generic) CancelEventArgs is in System.ComponentModel and exposes a boolean mutable Cancel argument that the event raiser can examine after each or all of the handlers are done. For the factory method, you'd still have to know what the generic signature of the Tuple needs to be, meaning you'd need 16 overloads of CreateArgs that would each accept a param list for a Tuple of the proper size and return an EventArgs<Tuple<T1,T2,...>>. As the parameters would be implied by the event, though, I do believe IntelliSense would be much easier to manage. \$\endgroup\$ – KeithS Oct 19 '11 at 18:06
  • \$\begingroup\$ After further review, there are built-in ways to create a Tuple from a param list. There are 8 such overloads, so you could quite feasibly have only 7 overloads for Tuple-based EventArgs (the single-parameter Tuple is trivial and to be discouraged in this particular case). That's pretty easy to manage, actually. And yes, since the Tuple itself is immutable you really only want the read-only side. \$\endgroup\$ – KeithS Oct 19 '11 at 18:11
  • \$\begingroup\$ Why do you feel var args = new EventArgs<SomeArg>(argument); is an Awkward syntax? \$\endgroup\$ – Ignacio Soler Garcia Jul 23 '15 at 9:14
1
\$\begingroup\$

Do you really need EventArgs concept? As for me, EventArgs is a legacy concept. When MS introduced generic Action and Func delegates I can hardly remember a situation, when I needed to use EventArgs again.

As far as I understand, you are not bounded to .net 2.0, because you are using Tuple. So if you can, consider using Action and Func.

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ Events as a construct are useful in UI classes because they show up as such in the designer, as opposed to delegate properties. \$\endgroup\$ – KeithS Apr 17 '12 at 21:34
  • \$\begingroup\$ Good suggestion. I never thought about using Action and Func for events. \$\endgroup\$ – Fathom Savvy Jul 12 '13 at 20:41
  • 1
    \$\begingroup\$ It's still recommended to use EventArgs msdn.microsoft.com/library/ms182133.aspx If you are talking about event/EventHandler, they natively handle multiple subscription and unsubscription which is harder with Action or Func. \$\endgroup\$ – Guillaume Jul 22 '14 at 7:46
  • \$\begingroup\$ @Guillaume Doesn't event/Action or event/Func also natively handle multiple subscription and unsubscription? (If not, I'm not sure what you are referring to.) \$\endgroup\$ – Jared Thirsk Oct 21 '19 at 22:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.