h3mm3's blog

Stories from behind the keyboard

  • RSS
  • Twitter

In C# you can easily unsubscribe a handler to an event using an handy (!) syntax. For instance let’s consider the following class, that exposes an event called MyEvent.

public class MyClass{
  public event EventHandler MyEvent;
  public void Fire() 
  {
    if (MyEvent!=null)
      MyEvent(this, EventArgs.Empty);
    else 
      Console.WriteLine("Nothing to do");
  } 
}

You can subscribe/unsuscribe to that event with the following code:

var myClass = new MyClass();
//Subscribing: 
myClass.MyEvent += MyMethod;

//Unsubscribing:
myClass.MyEvent –= MyMethod;

So far so good. Things gets complicated if you use an anonymous method, since you don't have a method identifier.

myClass.MyEvent+=(s,e)=>{
        Console.WriteLine("Gotcha!");
    };

myClass.MyEvent –= ??? //What's the price of anonymity?

Of course you can store a delegate to the anonymous method in a field or a local variable and use it when you need to unregister that handler:

var myClass = new MyClass();

var handler = (s,e) => { Console.WriteLine(“Gotcha!”); };
//Subscribing: 
myClass.MyEvent += handler;
//Unsubscribing:
myClass.MyEvent –= handler;

What I don’t like in this solution is that you have to store every delegate you think you will possibly unsubscribe. In effect those delegates (a.k.a. “Invocation list”) are yet stored somewhere (i.e. inside a MulticastDelegate - more info here) so storing them once again is kind of a repetition (and somewhat un-DRY-ish).

Unfortunately the only “code” authorized to peek inside a MulticastDelegate is the class that owns it. That’s to say: you cannot cycle through MyEvent’s handlers from outside MyClass.

One possible solution can be instrumenting your class (e.g. MyClass) in order to give that invocation list back to the caller. Let’s add the method GetMyEventHandlers() to MyClass and use some Linq:

public class MyClass 
{
  public event EventHandler MyEvent; 
 
  public void Fire() 
  {    
    if (MyEvent!=null)      
      MyEvent(this, EventArgs.Empty);    
    else
      Console.WriteLine("Nothing to do");  
  } 

  public IEnumerable<EventHandler> GetMyEventHandlers()
  {
    return from d in MyEvent.GetInvocationList() select (EventHandler)d;
  }  
}

In the previous code, note how the EventHandler delegate type associated to the event MyEvent is the same type used in the cast operator inside GetMyEventHandlers method.

Now we can simply ask MyClass the event invocation list and use it to unsubscribe our anonymous method.

//Subscribing an anonymous method:
myClass.MyEvent += (s,e)=>{ Console.WriteLine(“Gotcha!”) };

//Removing last handler:
myClass.MyEvent -= myClass.GetMyEventHandlers().Last();

The following code shows the whole definition of MyClass and an usage example; you can take the entire code and paste it inside LinqPad

//A working example...
void Main()
{
  var myClass = new MyClass();
  myClass.MyEvent += MyMethod;
  myClass.MyEvent+=(s,e)=>{
    Console.WriteLine("Gotcha!");
  };
  
  //Will execute 2 handlers
  myClass.Fire(); 
  
  //Removing last handler:
  myClass.MyEvent -= myClass.GetMyEventHandlers().Last();
  
  //Will execute just MyMethod
  myClass.Fire(); 
}

void MyMethod(object s, EventArgs e) 
{
  Console.WriteLine("I got you!");
}

public class MyClass 
{
  public event EventHandler MyEvent;
  
  public void Fire() 
  {
    Console.WriteLine("Fire!");
    if (MyEvent!=null)
      MyEvent(this, EventArgs.Empty);
    else 
      Console.WriteLine("Nothing to do");
  }
  
  public IEnumerable<EventHandler> GetMyEventHandlers()
  {
    return from d in MyEvent.GetInvocationList()
           select (EventHandler)d;
  }
}

As expected, the output of the previous example will be:

Fire! 
I got you! 
Gotcha! 
Fire! 
I got you! 

Happy programming!

No comments: