tags:

views:

64

answers:

4

I am using an event-based API and want to have a certain event handling method be called only once. (Note that I have another method which is always handling the event, but I want to add another handler for certain situations and have it only execute once.)

Is it possible/encouraged to unsubscribe from an event inside the event handler? E.g.

private void OnEventRaised(object sender, EventArgs e) {
    _eventRaisingObject.EventRaised -= OnEventRaised;
    ... // Do normal code
}

What sort of cross-threading issues should I be concerned with in this approach?

Secondly, is it possible to have the event handler be called only once when it is an anonymous method? E.g.

_eventRaisingObject.EventRaised += (sender, e) => {
    // Unsubscribe?
    ... // Do normal code
}

Currently, I am using an anonymous method and simply checking a boolean, but I think that it would be better (less overhead and potential bugs) to unsubscribe when the work is done - perhaps this assumption is incorrect. One problem I foresee with checking a boolean is that, if the situation needs repeating (so that the anonymous event handler is added again), the shared boolean might allow the anonymous methods to be called multiple times on different threads.

+2  A: 
EventHandler handler;
handler = (sender, e) => {
    _eventRaisingObject.EventRaised -= handler;

    // Do normal code
}
_eventRaisingObject.EventRaised += handler;

Should work because of closures capturing both handler and eventRaisingObject.

winSharp93
A: 

You can cast sender to the object that raised the event and unsubscribe.

void myRaisingObject_EventRaised(object sender, EventArgs e)
{
  (sender as MyRaisingObject).EventRaised -= myRaisingObject_EventRaised;
}
Ilia Jerebtsov
That's a good thing to note, but it isn't relevant to my original question. I have access to the object that is raising the event, but I want to know if it is a valid practice to unsubscribe from within the handler and if it is possible to do so with anonymous methods.
Pat
+1  A: 

Since the situation that requires the event to be handled can arise multiple times, and since threading is an issue, I would recommend putting a greater connection between the raising and the handling conditions.

I would not dynamically add and remove the event handler. It can certainly be done within the event handler, but it would be very difficult to make that robust in a multi-threaded environment.

But I definitely wouldn't be using a boolean for this either, for the same reasons.

You might consider creating a queue. Putting an item onto the queue to signal that the event handler needs to respond to the event and taking an item off of the queue when it is responded to (with proper locking on both sides, of course) you can have better control over the process.

Maybe that doesn't work for your circumstance, but it's something to consider.

Jeffrey L Whitledge
+1  A: 

Unsubscribing inside the event handler is possible, but there is an explicit race condition here. Another thread might be in the midst of calling the event handler as well, getting the handler called after unsubscribing is quite possible. You'll need to serialize access to the event delegate:

  public event EventHandler MyEvent;
  private object eventLock = new object();
...
  protected void OnMyEvent(EventArgs e) {
    lock(eventLock) {
      var handler = MyEvent;
      if (handler != null) MyEvent(this, e);
    }
  }

This isn't any different from protecting your boolean flag, although you can make that a lot more efficient with the Interlocked class.

Hans Passant