Unsubscribe anonymous method in C#

Viewed 83152

Is it possible to unsubscribe an anonymous method from an event?

If I subscribe to an event like this:

void MyMethod()
{
    Console.WriteLine("I did it!");
}

MyEvent += MyMethod;

I can un-subscribe like this:

MyEvent -= MyMethod;

But if I subscribe using an anonymous method:

MyEvent += delegate(){Console.WriteLine("I did it!");};

is it possible to unsubscribe this anonymous method? If so, how?

14 Answers
Action myDelegate = delegate(){Console.WriteLine("I did it!");};

MyEvent += myDelegate;


// .... later

MyEvent -= myDelegate;

Just keep a reference to the delegate around.

One technique is to declare a variable to hold the anonymous method which would then be available inside the anonymous method itself. This worked for me because the desired behavior was to unsubscribe after the event was handled.

Example:

MyEventHandler foo = null;
foo = delegate(object s, MyEventArgs ev)
    {
        Console.WriteLine("I did it!");
        MyEvent -= foo;
    };
MyEvent += foo;

From memory, the specification explicitly doesn't guarantee the behaviour either way when it comes to equivalence of delegates created with anonymous methods.

If you need to unsubscribe, you should either use a "normal" method or retain the delegate somewhere else so you can unsubscribe with exactly the same delegate you used to subscribe.

In 3.0 can be shortened to:

MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;

Kind of lame approach:

public class SomeClass
{
  private readonly IList<Action> _eventList = new List<Action>();

  ...

  public event Action OnDoSomething
  {
    add {
      _eventList.Add(value);
    }
    remove {
      _eventList.Remove(value);
    }
  }
}
  1. Override the event add/remove methods.
  2. Keep a list of those event handlers.
  3. When needed, clear them all and re-add the others.

This may not work or be the most efficient method, but should get the job done.

If the best way is to keep a reference on the subscribed eventHandler, this can be achieved using a Dictionary.

In this example, I have to use a anonymous method to include the mergeColumn parameter for a set of DataGridViews.

Using the MergeColumn method with the enable parameter set to true enables the event while using it with false disables it.

static Dictionary<DataGridView, PaintEventHandler> subscriptions = new Dictionary<DataGridView, PaintEventHandler>();

public static void MergeColumns(this DataGridView dg, bool enable, params ColumnGroup[] mergedColumns) {

    if(enable) {
        subscriptions[dg] = (s, e) => Dg_Paint(s, e, mergedColumns);
        dg.Paint += subscriptions[dg];
    }
    else {
        if(subscriptions.ContainsKey(dg)) {
            dg.Paint -= subscriptions[dg];
            subscriptions.Remove(dg);
        }
    }
}

There is a way to solve this by implementing the closure yourself instead of a lambda expression.

Assume that the class to be used as a capture variable is as follows.

public class A
{
    public void DoSomething()
    {
        ...
    }
}

public class B
{
    public void DoSomething()
    {
        ...
    }
}

public class C
{
    public void DoSomething()
    {
        ...
    }
}

These classes will be used as capture variables, so we instantiate them.

A a = new A();
B b = new B();
C c = new C();

Implement the closure class as shown below.

private class EventHandlerClosure
{
    public A a;
    public B b;
    public C c;

    public event EventHandler Finished;

    public void MyMethod(object, MyEventArgs args)
    {
        a.DoSomething();
        b.DoSomething();
        c.DoSomething();
        Console.WriteLine("I did it!");

        Finished?.Invoke(this, EventArgs.Empty);
    }
}

Instantiate the closure class, create a handler, then subscribe to the event and subscribe to the lambda expression that unsubscribes from the closure class's Finished event.

var closure = new EventHandlerClosure
{
    a = a,
    b = b,
    c = c
};
var handler = new MyEventHandler(closure.MyMethod);
MyEvent += handler;
closure.Finished += (s, e)
{
    MyEvent -= handler;
}

I discovered this quite old thread recently for a C# project and found all the answers very useful. However, there was one aspect that didn't work well for my particular use case - they all put the burden of unsubscribing from an event on the subscriber. I understand that one could make the argument that it's the subscribers job to handle this, however that isn't realistic for my project.

My primary use case for events is for listening to timers to sequence animations (it's a game). In this scenario, I use a lot of anonymous delegates to chain together sequences. Storing a reference to these isn't very practical.

In order to solve this, I've created a wrapper class around an event that lets you subscribe for a single invocation.

internal class EventWrapper<TEventArgs> {
    
    private event EventHandler<TEventArgs> Event;
    private readonly HashSet<EventHandler<TEventArgs>> _subscribeOnces;
    
    internal EventWrapper() {
        _subscribeOnces = new HashSet<EventHandler<TEventArgs>>();
    }

    internal void Subscribe(EventHandler<TEventArgs> eventHandler) {
        Event += eventHandler;
    }

    internal void SubscribeOnce(EventHandler<TEventArgs> eventHandler) {
        _subscribeOnces.Add(eventHandler);
        Event += eventHandler;
    }

    internal void Unsubscribe(EventHandler<TEventArgs> eventHandler) {
        Event -= eventHandler;
    }

    internal void UnsubscribeAll() {
        foreach (EventHandler<TEventArgs> eventHandler in Event?.GetInvocationList()) {
            Event -= eventHandler;
        }
    }

    internal void Invoke(Object sender, TEventArgs e) {
        Event?.Invoke(sender, e);
        if(_subscribeOnces.Count > 0) {
            foreach (EventHandler<TEventArgs> eventHandler in _subscribeOnces) {
                Event -= eventHandler;
            }
            _subscribeOnces.Clear();
        }
    }

    internal void Remove() {
        UnsubscribeAll();
        _subscribeOnces.Clear();
    }
}

The side benefit of having this in a class is that you can make it private and expose only the functionality you want. For example, only expose the SubscribeOnce (and not the Subscribe) method.

public class MyClass {
    
    private EventWrapper<MyEventEventArgs> myEvent = new EventWrapper<MyEventEventArgs>();
    
    public void FireMyEvent() {
        myEvent.Invoke(this, new MyEventEventArgs(1000, DateTime.Now));
    }
    
    public void SubscribeOnce(EventHandler<MyEventEventArgs> eventHandler) {
        myEvent.SubscribeOnce(eventHandler);
    }
    
    public class MyEventEventArgs : EventArgs {
        public int MyInt;
        public DateTime MyDateTime;
        
        public MyEventEventArgs(int myInt, DateTime myDateTime) {
            MyInt = myInt;
            MyDateTime = myDateTime;
        }
    }
}

The tradeoff here is more overhead for having an instance of this for each event, however in my scenario - this is an acceptable tradeoff to ensure that garbage gets collected efficiently and the code is more maintainable on the subscriber side. Full example here.

Here is a simple solution, which removes all assigned methods from an event. Also anonymous methods.

Use this code and adjust the names.

if (MyEvent != null)
    foreach (Delegate del in MyEvent.GetInvocationList())
        MyEvent -= (EventHandler<MyEventHandlerType>)del;

Example usage

public class SomeClass
{
  public event EventHandler<NiceEventArgs> NiceEvent;

  public void RemoveHandlers()
  {
    if (NiceEvent != null)
      foreach (Delegate del in NiceEvent.GetInvocationList())
        NiceEvent -= (EventHandler<NiceEventArgs>)del;
  }
}

Thanks to hemme's answer, which I used as inspiration.

Related