Removing all event handlers in one go

19,498

Solution 1

You can use Delegate.RemoveAll(). (The part you're interested in is in button2_Click)

public void Form_Load(object sender, EventArgs e) 
{ 
    button1.Click += new EventHandler(button1_Click);
    button1.Click += new EventHandler(button1_Click);
    button2.Click += new EventHandler(button2_Click);
    TestEvent += new EventHandler(Form_TestEvent);
}
event EventHandler TestEvent;
void OnTestEvent(EventArgs e)
{
    if (TestEvent != null)
        TestEvent(this, e);
}
void Form_TestEvent(object sender, EventArgs e)
{
    MessageBox.Show("TestEvent fired");
}
void button2_Click(object sender, EventArgs e)
{
    Delegate d = TestEvent as Delegate;
    TestEvent = Delegate.RemoveAll(d, d) as EventHandler;
}
void button1_Click(object sender, EventArgs e)
{
    OnTestEvent(EventArgs.Empty);
}

You should note that it doesn't alter the contents of the delegates you pass in to it, it returns an altered delegate. Consequently, you won't be able to alter the events on a button you've dropped on a form from the form, as button1.Click can only have += or -= used on it, not =. This won't compile:

button1.Click = Delegate.RemoveAll(d, d) as EventHandler;

Also, be sure that wherever you're implementing this you're watching out for the potential of race conditions. You could end up with some really strange behavior if you're removing handlers from an event that is being called by another thread!

Solution 2

public class TheAnswer
{
    public event EventHandler MyEvent = delegate { };

    public void RemoveFromMyEvent(string methodName)
    {
        foreach (var handler in MyEvent.GetInvocationList())
        {
            if (handler.Method.Name == methodName)
            {
                MyEvent -= (EventHandler)handler;
            }
        }
    }
}

EDIT 2: Apologies for my misunderstanding--I see that you were pretty clear about not having access to the event sources in your original post.

The simplest way I can think of to solve this problem involves implementing a Shared dictionary of object-to-document bindings. When an object enters a document, check the dictionary for an existing binding to another document; if present, remove handlers that refer to the old document before adding them for the new. Either way, update the dictionary with the new binding.

I think in most cases the performance and memory impacts would be negligible: unless you're dealing with many tens of thousands of small objects and frequently exchange them between documents, the memory overhead of each key/value pair and performance hit for each lookup operation should be fairly small.

As an alternative: if you can detect (in the document event handlers) that the sender of the event is no longer relevant to the document, you can detach the events there.

These seem like the kind of ideas you might have already rejected--but maybe not!

Solution 3

Use Delegate.RemoveAll (maybe using reflection if the Delegate instance is private).

Share:
19,498
David Rutten
Author by

David Rutten

Bit-smith at Robert McNeel & Associates. Developer of the Grasshopper Algorithmic Modeling plug-in for the Rhinoceros 3D CAD application. I also keep a personal blog where not absolutely everything is about programming.

Updated on June 04, 2022

Comments

  • David Rutten
    David Rutten almost 2 years

    Problem: I have a document class which contains a list of objects. These objects raise events such as SolutionExpired, DisplayExpired etc. The document needs to respond to this.

    Documents can sometimes exchange objects, but a single object should never be 'part' of more than one document.

    My document class contains a bunch of methods which serve as event handlers. Whenever an object enters the document, I use AddHandler to set up the events, and whenever an object is removed from the document I use RemoveHandler to undo the damage. However, there are cases where it's difficult to make sure all the steps are properly taken and I might thus end up with rogue event handlers.

    Long story short; how do I remove all the handlers that are pointing to a specific method? Note, I don't have a list of potential event sources, these could be stored anywhere.

    Something like:

    RemoveHandler *.SolutionExpired, AddressOf DefObj_SolutionExpired
    
  • David Rutten
    David Rutten almost 15 years
    Ben, thanks for posting this. I'm afraid it's not making a lot of sense to me (or the VB compiler for that matter). Could you please post the original C#?
  • Ben M
    Ben M almost 15 years
    I knew I should have left the original C# version up. :-)
  • David Rutten
    David Rutten over 14 years
    Because I need to have an instance of the source for this. I think... I no longer have access to the event raising object, only the handler.
  • Ben M
    Ben M over 14 years
    I don't get how this is acceptable, given the OP's condition of needing to remove handlers from an event to which he doesn't have private-level access. I must be missing something!
  • user81993
    user81993 about 8 years
    Also this is C# while OP's question was about vb.net. While the two have a lot of overlap, event syntax is one where they greatly differ in