views:

155

answers:

4

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?

+1  A: 

You can use an anonymous method instead:

Event += (sender, e) =>
{
     // Multiple lines
     // of code here
};
nasufara
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
declare the variable as an EventHandler and you should be OK...
Thomas Levesque
+5  A: 

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
Except that e2 now goes through two function calls to actually do anything... eww.
Matthew Scharley
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
@Matthew Scharley - have you measured the overhead?
Daniel Earwicker
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?
Snarfblam
@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
@Cameron - it has not. The new 4.0 covariance translates between `Func<string>` and `Func<object>` where appropriate, but that is because they are both kinds of `Func<T>` 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
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
+2  A: 

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
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
+1  A: 
Action<Object, EventArgs> a = (o, ea) => { };
EventHandler e = a.Invoke;
QrystaL