37

I was wiring up an event to use a lambda which needed to remove itself after triggering. I couldn't do it by inlining the lambda to the += event (no accessable variable to use to remove the event) so i set up an Action<object, EventArgs> variable and moved the lambda there. The main error was that it could not convert an Action<object, EventArgs> to an EventHandler. I thought lambda expressions were implicitly convertable to event handlers, why doesn't this work?

ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
RCIX
  • 38,647
  • 50
  • 150
  • 207

5 Answers5

51
Action<Object, EventArgs> a = (o, ea) => { };
EventHandler e = a.Invoke;
QrystaL
  • 4,886
  • 2
  • 24
  • 28
45

Lambdas are implicitly convertible to delegate types with the right shape, but two same-shaped delegate types are not implicitly convertible to one another. Just make the local variable have type EventHandler instead.

EventHandler h = (o, ea) => { ... };
e += h;
...
e -= h;

(in case it helps:

Action<object, EventArgs> a = (o, ea) => { }; 
EventHandler e = a;  // not allowed
EventHandler e2 = (o,ea) => a(o,ea);  // ok

)

Brian
  • 117,631
  • 17
  • 236
  • 300
  • 2
    Except that e2 now goes through two function calls to actually do anything... eww. – Matthew Scharley Oct 11 '09 at 22:47
  • 3
    I'm not promoting e2 as a good way to write code, I'm just using it as an example to demonstrate what is and is not legal with regards to the C# type system. – Brian Oct 11 '09 at 23:06
  • 1
    @Matthew Scharley - have you measured the overhead? – Daniel Earwicker Oct 11 '09 at 23:16
  • 5
    It strikes me as a bit silly that you can't cast from one delegate type to another that is equivalent. Is there any good reason for this? – snarf Oct 12 '09 at 00:58
  • @Snarfblam: I'm guessing it's the covariance issue, as delegates are generics. I wonder if this issue has gone in .NET 4. – Cameron MacFarland Oct 12 '09 at 09:40
  • @Cameron - it has not. The new 4.0 covariance translates between `Func` and `Func` where appropriate, but that is because they are both kinds of `Func` delegate. If they used a different named delegate type, they would be incompatible. The simple solution is to always use `Func` or `Action` to represent delegates. They allow up to 8 parameters in 4.0. The only limitation of them is that they don't allow the parameters to be `out` or `ref`, which is possible with custom named delegate types. – Daniel Earwicker Oct 14 '09 at 09:36
  • 1
    On whether there is much value in using custom delegate types for events, see http://stackoverflow.com/questions/1120506/what-would-i-lose-by-abandoning-the-standard-eventhandler-pattern-in-net – Daniel Earwicker Oct 14 '09 at 09:39
  • @MatthewScharley - OTOH, the traditional `handler += myMethod` actually compiles into `handler += new EventHandler(myMethod)` - so the overhead is perhaps not that different. – ToolmakerSteve Jan 06 '20 at 20:01
3

Declare your event as

public event Action<object, EventArgs> e;

Then you can directly add your action:

Action<object, EventArgs> a = something;
e += a;
olaf
  • 531
  • 4
  • 4
2

In general, delegates can't be cast because they have no inheritance tree defining which casts are valid. To that end, you have two choices:

  1. Use a variable of type EventHandler instead of the Action<T1, T2>
  2. Use an inline declaration.

    // option 1: local variable
    EventHandler eh = (o, ea) => { /* [snip] */ };
    obj.event += eh;
    obj.event -= eh;
    
    // option 2: inline declaration
    obj.event += (o, ea) => { /* [snip] */ };
    
Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
  • The whole point of using a variable was so that i could remove the event from the event handler while still inside the event. Still, +1. – RCIX Oct 11 '09 at 22:36
-1

You can use an anonymous method instead:

Event += (sender, e) =>
{
     // Multiple lines
     // of code here
};
  • 2
    That's the whole point.... I have one set up only stored into a variable to i can have the event remove itself when fired, only it won't work. – RCIX Oct 11 '09 at 22:29