EventHandler<T> or Action<T>

If you’ve used C# for any length of time, you’ve used events. Most likely, you wrote something like this:

public class MyCoolCSharpClass {
     public event EventHandler MyCoolEvent;
}

public class MyOtherClass {
     public void MyOtherMethod(MyCoolCSharpClass obj)
     {
          obj.MyCoolEvent += WhenTheEventFires;
     }

     private void WhenTheEventFires(object sender, EventArgs args)
     {
          Console.WriteLine("Hello World!");
     }
}

Later, you need parameters to be passed in along with the event, so you changed it to something like this:

public event EventHandler<MyEventArgs> MyCoolEvent;

public class MyEventArgs : EventArgs 
{
     public string Name { get; set; }
     public DateTime WhenSomethingHappened { get; set; }
}
...
     private void WhenTheEventFires(object sender, MyEventArgs args)
     {
          var theCoolCSharpSendingClass = (MyCoolCSharpClass)sender;
          Console.WriteLine("Hello World! Good to meet you " + args.Name);
     }

You add two or three more events, some property change and changing events, and finally a class with about 4 properties, 3 events, and a little bit of code now has 3 supporting EventArgs classes, casts for every time you need the sender class instance (In this example, I’m assuming the event is always fired by MyCoolCSharpClass, and not through a method from a 3rd class). There’s a lot of code there to maintain even for just a simple class with some very simple functionality.

Lets look at this for a minute. First, EventHandler and EventHandler<T> are simply delegates, nothing more nothing less (If you’re not sure what a delegate is, don’t sweat it, it’s not really the point of this discussion). What makes the magic happen for events is that little event keyword the prefaces the event that turns that internally turns the delegate type into a subscribe-able field. Essentially, it simplifies adding and removing multiple methods that are all called when the event is invoked. With the introduction of generics in C# 2.0, and the introduction of LINQ in 3.5, we have generic forms of most of the delegates we could ever use in the form of Action<T1, T2, T3...> and Func<TRes, T1, T2...>. What this means, is that we can change an event declarations to use whatever delegate we want. Something like this is perfectly valid:

public event Action<MyCoolCSHarpClass, string, DateTime> MyCoolEvent;

And what about when we subscribe? Well, now we get typed parameters:

...
     private void WhenTheEventFires(MyCoolCSHarpClass sender, string name, DateTime theDate)
     {
          Console.WriteLine("Hello World! Good to meet you " + name);
     }

That’s cool. I’ve now reduced the amount of code I have to maintain from 4 classes to 1 and I don’t have to cast my sender. As a matter of fact, I don’t even have to pass a sender. How often have you written an event that’s something like this:

public event EventHandler TheTableWasUpdatedGoCheckIt;

Whoever is subscribed to this event doesn’t care about who sent it, or what data specifically was updated, all the subscribe cares about was that it was fired, nothing more than that. Even then, in a “you can only use EventHandler delegate world” you’re still stuck creating a method to subscribe to the event that looks like this:

     private void WhenTheTableWasUpdated(object sender, EventArgs args)
     {
          // Go check the database and update stuff...
     }

If we use what we’ve learned and change the event to something like this:

public event Action TheTableWasUpdatedGoCheckIt;

We can write our method like this:

     private void WhenTheTableWasUpdated()
     {
          // Go check the database and update stuff...
     }

Since we never cared about the parameters in the first place.

Thats awesome fine and dandy, but just blindly replacing every instance of EventHandler delegates to Actions isn’t always the best idea, there are a few caveats:

First, there are some practical physical limitations of using Action<T1, T2, T2... > vs using a derived class of EventArgs, three main ones that I can think of:

  • If you change the number or types of parameters, every method that subscribes to that event will have to be changed to conform to the new signature. If this is a public facing event that 3rd party assemblies will be using, and there is any possibility that the number or type of arguments would change, its a very good reason to use a custom class that can later be inherited from to provide more parameters. Remember, you can still use an Action<MyCustomClass>, but deriving from EventArgs is still the Way Things Are Done
  • Using Action<T1, T2, T2... > will prevent you from passing feedback BACK to the calling method unless you have a some kind of object (with a Handled property for instance) that is passed along with the Action, and if you’re going to make a class with a handled property, making it derive from EventArgs is completely reasonable.
  • You don’t get named parameters by using Action<T1, T2 etc...> so if you’re passing 3 bool‘s, an int, two string‘s, and a DateTime, you won’t immediately know what the meaning of those values. Passing a custom args class provides meaning to those parameters.

Secondly, consistency implications. If you have a large system you’re already working with, it’s nearly always better to follow the way the rest of the system is designed unless you have an very good reason not too. If you have publicly facing events that need to be maintained, the ability to substitute derived classes for args might be important.

Finally, real life practice, I personally find that I tend to create a lot of one off events for things like property changes that I need to interact with (Particularly when doing MVVM with view models that interact with each other) or where the event has a single parameter. Most of the time these events take on the form of public event Action<[classtype], bool> [PropertyName]Changed; or public event Action SomethingHappened;. In these cases, there are two benefits that you might be able to guess from what you’ve already seen.

  • I get a type for the issuing class. If MyClass declares and is the only class firing the event, I get an explicit instance of MyClass to work with in the event handler.
  • For simple events such as property change events, the meaning of the parameters is obvious and stated in the name of the event handler and I don’t have to create a myriad of classes for these kinds of events.

Food for thought. If you have any comments, feel free to leave them in the comment section below.