Pass an event as a parameter to a method [duplicate]

You have in fact discovered that events are not "first class" in C#; you cannot pass around an event as data. You can pass around a delegate to a method associated with a receiver as a first-class object by making a delegate. You can pass around a reference to any variable as a (mostly) first-class object. (I say "mostly" because references to variables cannot be stored in fields, stored in arrays, and so on; they are highly restricted compared to other kinds of data.) You can pass around a type by obtaining its Type object and passing that around.

But there is no way to directly pass around as data an event, property, indexer, constructor or destructor associated with a particular instance. The best you can do is to make a delegate (or pair of delegates) out of a lambda, as you suggest. Or, obtain the reflection object associated with the event and pass that around, along with the instance.


No, unfortunately not.

If you look at Reactive Extensions, that suffers from a similar problem. Three options they use (IIRC - it's been a while since I've looked):

  • Pass in the corresponding EventInfo and call it with reflection
  • Pass in the name of the event (and the target if necessary) and call it with reflection
  • Pass in delegates for subscription and unsubscription

The call in the latter case would be something like:

SubscribeAndDoUnsubscribe(elements,
                          handler => e.Click += handler,
                          handler => e.Click -= handler);

and the declaration would be:

void SubscribeDoAndUnsubscribe<TElement, TEventArgs>(
        IEnumerable<TElement> elements,
        Action<EventHandler<TEventArgs>> subscription,
        Action<EventHandler<TEventArgs>> unsubscription)
    where TEventArgs: EventArgs

You're trying to get around type safety, and you can't do so without using reflection. I'll show you an even simpler example of what you're trying to do.

void DoSomethingOnSomethingElse(T obj, Action method)
{
    obj.method();
}

C# doesn't work this way. How does the compiler know that all Ts have the method method? It doesn't, and can't. Similarly, not every TElement in your code will have an event Click for example.

It sounds like you just want to set a single use event handler on a set of objects. You can do this quite easily...

EventHandler handler = null;
handler = (s,e) => 
{
    DoSomething(e);
    var b = (Button) s;
    b.Click -= handler;
}

foreach (var button in buttons) 
{
    button.Click += handler;
}

This, obviously, only works with buttons, but as I write this, I see Jon Skeet has shown you a more general solution, so I'll end here.