Come rimuovere tutti i gestori di eventi da un evento


367

Per creare un nuovo gestore eventi su un controllo, è possibile farlo

c.Click += new EventHandler(mainFormButton_Click);

o questo

c.Click += mainFormButton_Click;

e per rimuovere un gestore di eventi puoi farlo

c.Click -= mainFormButton_Click;

Ma come si rimuovono tutti i gestori di eventi da un evento?


10
Se qualcuno è venuto qui alla ricerca di una soluzione WPF, potresti voler dare una occhiata a questa risposta .
Douglas,

1
Non puoi semplicemente impostare c.Click = null?
alexania,

Questa è una di quelle cose che trovo ridicolmente eccessivamente complicate. ClearApparentemente un metodo semplice era troppo sforzo
Zimano il

Risposte:


167

Ho trovato una soluzione sui forum MSDN . Il seguente codice di esempio rimuoverà tutti gli Clickeventi da button1.

public partial class Form1 : Form
{
        public Form1()
        {
            InitializeComponent();

            button1.Click += button1_Click;
            button1.Click += button1_Click2;
            button2.Click += button2_Click;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Hello");
        }

        private void button1_Click2(object sender, EventArgs e)
        {
            MessageBox.Show("World");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            RemoveClickEvent(button1);
        }

        private void RemoveClickEvent(Button b)
        {
            FieldInfo f1 = typeof(Control).GetField("EventClick", 
                BindingFlags.Static | BindingFlags.NonPublic);
            object obj = f1.GetValue(b);
            PropertyInfo pi = b.GetType().GetProperty("Events",  
                BindingFlags.NonPublic | BindingFlags.Instance);
            EventHandlerList list = (EventHandlerList)pi.GetValue(b, null);
            list.RemoveHandler(obj, list[obj]);
        }
    }
}

Se button1 è impostato su null, tutti i gestori eventi sono collegati a button1. Fare clic su correttamente?
Damien,

3
Correggimi se sbaglio, ma la prima linea di RemoveClickEventpartenza non dovrebbe iniziare con FieldInfo f1 = typeof(Button):? Ottengo null da GetFieldse uso Control.
Protettore l'

2
Questo non sembra funzionare con ToolStripButtons. Ho sostituito Button in RemoveClickEvent con ToolStripButton, ma gli eventi sono ancora attivi dopo aver chiamato RemoveClickEvent. Qualcuno ha una soluzione per questo problema?
Skalli,

1
il link sopra in MSDN suggerisce anche di provare myButton.Click + = null; se vuoi rimuovere tutti i delegati (non per Click, ma per altri eventi ..)
hello_earth

1
@hello_earth Non sembra funzionare perObservableCollection.CollectionChanged += null;
Mike de Klerk il

146

Ragazzi, state rendendo questo MODO troppo duro per voi stessi. È così facile:

void OnFormClosing(object sender, FormClosingEventArgs e)
{
    foreach(Delegate d in FindClicked.GetInvocationList())
    {
        FindClicked -= (FindClickedHandler)d;
    }
}

58
Funzionerebbe solo se possiedi l'evento. Prova a farlo su un controllo.
Delyan,

227
... e se possiedi l'evento, puoi semplicemente scrivere FindClicked = null;che è piuttosto semplice.
Jon Skeet,

79
Che cos'è FindClicked?
Levitikon,

3
Questo non funziona per gli eventi Kinect kinect.ColorFrameReady -= MyEventHander, ma non esiste un GetInvocationList()metodo sulle istanze Kinect per scorrere i propri delegati.
Brent Faust,

GetInvocationListnon trovato.
Joke Huang,

75

Dalla rimozione di tutti i gestori di eventi :

Direttamente no, in gran parte perché non è possibile semplicemente impostare l'evento su null.

Indirettamente, potresti rendere privato l'evento reale e creare una proprietà attorno ad esso che tenga traccia di tutti i delegati che vengono aggiunti / sottratti ad esso.

Prendi il seguente:

List<EventHandler> delegates = new List<EventHandler>();

private event EventHandler MyRealEvent;

public event EventHandler MyEvent
{
    add
    {
        MyRealEvent += value;
        delegates.Add(value);
    }

    remove
    {
        MyRealEvent -= value;
        delegates.Remove(value);
    }
}

public void RemoveAllEvents()
{
    foreach(EventHandler eh in delegates)
    {
        MyRealEvent -= eh;
    }
    delegates.Clear();
}

4
Pensavo che l'OP si riferisse a controlli .net generali in cui questo tipo di avvolgimento potrebbe non essere possibile.
Gishu,

4
potresti ottenere il controllo, quindi sarebbe
Tom Fobear,

Questo porta anche a mantenere due elenchi, consultare stackoverflow.com/questions/91778/… per il ripristino o stackoverflow.com/questions/91778/… per accedere all'elenco.
TN.

63

La risposta accettata non è piena. Non funziona per eventi dichiarati come {add; rimuovere;}

Ecco il codice di lavoro:

public static void ClearEventInvocations(this object obj, string eventName)
{
    var fi = obj.GetType().GetEventField(eventName);
    if (fi == null) return;
    fi.SetValue(obj, null);
}

private static FieldInfo GetEventField(this Type type, string eventName)
{
    FieldInfo field = null;
    while (type != null)
    {
        /* Find events defined as field */
        field = type.GetField(eventName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
        if (field != null && (field.FieldType == typeof(MulticastDelegate) || field.FieldType.IsSubclassOf(typeof(MulticastDelegate))))
            break;

        /* Find events defined as property { add; remove; } */
        field = type.GetField("EVENT_" + eventName.ToUpper(), BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
        if (field != null)
            break;
        type = type.BaseType;
    }
    return field;
}

4
QUESTA versione funzionava per me. La versione accettata non ha funzionato. +1 per quello.
Meister Schnitzel,

1
Non ha funzionato per gli eventi WPF fino a quando non l'ho usato BindingFlags.Publicnella prima GetFieldchiamata.
Lennart,

40

Non è dannoso eliminare un gestore eventi inesistente. Quindi, se sai quali gestori potrebbero esserci, puoi semplicemente eliminarli tutti. Ho appena avuto un caso simile. Questo può aiutare in alcuni casi.

Piace:

// Add handlers...
if (something)
{
    c.Click += DoesSomething;
}
else
{
    c.Click += DoesSomethingElse;
}

// Remove handlers...
c.Click -= DoesSomething;
c.Click -= DoesSomethingElse;

16

In realtà sto usando questo metodo e funziona perfettamente. Sono stato "ispirato" dal codice scritto da Aeonhack qui .

Public Event MyEvent()
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
    If MyEventEvent IsNot Nothing Then
        For Each d In MyEventEvent.GetInvocationList ' If this throws an exception, try using .ToArray
            RemoveHandler MyEvent, d
        Next
    End If
End Sub

Il campo MyEventEvent è nascosto, ma esiste.

Debug, puoi vedere come d.targetl'oggetto sta realmente gestendo l'evento e il d.methodsuo metodo. Devi solo rimuoverlo.

Funziona benissimo. Niente più oggetti non sottoposti a GC a causa dei gestori di eventi.


3
Si prega di non scrivere risposte in altre lingue.
Hille,

10

Ho odiato tutte le soluzioni complete mostrate qui, ho fatto un mix e testato ora, ho lavorato per qualsiasi gestore di eventi:

public class MyMain()
    public void MyMethod() {
        AnotherClass.TheEventHandler += DoSomeThing;
    }

    private void DoSomething(object sender, EventArgs e) {
        Debug.WriteLine("I did something");
        AnotherClass.ClearAllDelegatesOfTheEventHandler();
    }

}

public static class AnotherClass {

    public static event EventHandler TheEventHandler;

    public static void ClearAllDelegatesOfTheEventHandler() {

        foreach (Delegate d in TheEventHandler.GetInvocationList())
        {
            TheEventHandler -= (EventHandler)d;
        }
    }
}

Facile! Grazie per Stephen Punak.

L'ho usato perché utilizzo un metodo locale generico per rimuovere i delegati e il metodo locale è stato chiamato dopo diversi casi, quando sono impostati diversi delegati.


4

Se devi assolutamente farlo ... ci vorrà un po 'di tempo per riflettere. I gestori di eventi sono gestiti in una mappa da delegare ad evento all'interno di un controllo. Dovresti farlo

  • Rifletti e ottieni questa mappa nell'istanza di controllo.
  • Scorrere per ogni evento, ottenere il delegato
    • ogni delegato a sua volta potrebbe essere una serie incatenata di gestori di eventi. Quindi chiama obControl.RemoveHandler (evento, gestore)

In breve, molto lavoro. In teoria è possibile ... Non ho mai provato qualcosa del genere.

Vedi se riesci ad avere un migliore controllo / disciplina sulla fase di iscrizione-annullamento iscrizione per il controllo.


3

Stephen ha ragione. È molto facile:

public event EventHandler<Cles_graph_doivent_etre_redessines> les_graph_doivent_etre_redessines;
public void remove_event()
{
    if (this.les_graph_doivent_etre_redessines != null)
    {
        foreach (EventHandler<Cles_graph_doivent_etre_redessines> F_les_graph_doivent_etre_redessines in this.les_graph_doivent_etre_redessines.GetInvocationList())
        {
            this.les_graph_doivent_etre_redessines -= F_les_graph_doivent_etre_redessines;
        }
    }
}

38
Dio, il compilatore dovrebbe vietare quel tipo di nomi di variabili. graphs_must_be_redrawn in francese.
gracco

4
Traduzione dal francese foreach (EventHandler<MyCompletedArgs> handler in CompletionCompleted.GetInvocationList()) { CompletionCompleted -= handler; }
Anton K,

La traduzione inglese di @AntonK funziona bene. Ricordarsi di verificare la presenza di null sul gestore proprietà.
Brett,

2

Ho appena trovato Come sospendere gli eventi quando si imposta una proprietà di un controllo WinForms . Rimuoverà tutti gli eventi da un controllo:

namespace CMessWin05
{
    public class EventSuppressor
    {
        Control _source;
        EventHandlerList _sourceEventHandlerList;
        FieldInfo _headFI;
        Dictionary<object, Delegate[]> _handlers;
        PropertyInfo _sourceEventsInfo;
        Type _eventHandlerListType;
        Type _sourceType;


        public EventSuppressor(Control control)
        {
            if (control == null)
                throw new ArgumentNullException("control", "An instance of a control must be provided.");

            _source = control;
            _sourceType = _source.GetType();
            _sourceEventsInfo = _sourceType.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
            _sourceEventHandlerList = (EventHandlerList)_sourceEventsInfo.GetValue(_source, null);
            _eventHandlerListType = _sourceEventHandlerList.GetType();
            _headFI = _eventHandlerListType.GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
        }

        private void BuildList()
        {
            _handlers = new Dictionary<object, Delegate[]>();
            object head = _headFI.GetValue(_sourceEventHandlerList);
            if (head != null)
            {
                Type listEntryType = head.GetType();
                FieldInfo delegateFI = listEntryType.GetField("handler", BindingFlags.Instance | BindingFlags.NonPublic);
                FieldInfo keyFI = listEntryType.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic);
                FieldInfo nextFI = listEntryType.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic);
                BuildListWalk(head, delegateFI, keyFI, nextFI);
            }
        }

        private void BuildListWalk(object entry, FieldInfo delegateFI, FieldInfo keyFI, FieldInfo nextFI)
        {
            if (entry != null)
            {
                Delegate dele = (Delegate)delegateFI.GetValue(entry);
                object key = keyFI.GetValue(entry);
                object next = nextFI.GetValue(entry);

                Delegate[] listeners = dele.GetInvocationList();
                if(listeners != null && listeners.Length > 0)
                    _handlers.Add(key, listeners);

                if (next != null)
                {
                    BuildListWalk(next, delegateFI, keyFI, nextFI);
                }
            }
        }

        public void Resume()
        {
            if (_handlers == null)
                throw new ApplicationException("Events have not been suppressed.");

            foreach (KeyValuePair<object, Delegate[]> pair in _handlers)
            {
                for (int x = 0; x < pair.Value.Length; x++)
                    _sourceEventHandlerList.AddHandler(pair.Key, pair.Value[x]);
            }

            _handlers = null;
        }

        public void Suppress()
        {
            if (_handlers != null)
                throw new ApplicationException("Events are already being suppressed.");

            BuildList();

            foreach (KeyValuePair<object, Delegate[]> pair in _handlers)
            {
                for (int x = pair.Value.Length - 1; x >= 0; x--)
                    _sourceEventHandlerList.RemoveHandler(pair.Key, pair.Value[x]);
            }
        }

    }
}

1
Questo è stato molto utile, ma c'è una cosa che deve essere cambiata: in Resume (), stai aggiungendo i gestori in ordine inverso (suppongo sia una copia / incolla da Suppress, dove vuoi lavorare all'indietro quindi come non pasticciare con una raccolta su cui stai ripetendo). Alcuni codici contano sul fatto che i gestori si attivino in un determinato ordine, quindi non si dovrebbe sbagliare.
Michael,

1

Wow. Ho trovato questa soluzione, ma niente ha funzionato come volevo. Ma è così bello:

EventHandlerList listaEventos;

private void btnDetach_Click(object sender, EventArgs e)
{
    listaEventos = DetachEvents(comboBox1);
}

private void btnAttach_Click(object sender, EventArgs e)
{
    AttachEvents(comboBox1, listaEventos);
}

public EventHandlerList DetachEvents(Component obj)
{
    object objNew = obj.GetType().GetConstructor(new Type[] { }).Invoke(new object[] { });
    PropertyInfo propEvents = obj.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);

    EventHandlerList eventHandlerList_obj = (EventHandlerList)propEvents.GetValue(obj, null);
    EventHandlerList eventHandlerList_objNew = (EventHandlerList)propEvents.GetValue(objNew, null);

    eventHandlerList_objNew.AddHandlers(eventHandlerList_obj);
    eventHandlerList_obj.Dispose();

    return eventHandlerList_objNew;
}

public void AttachEvents(Component obj, EventHandlerList eventos)
{
    PropertyInfo propEvents = obj.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);

    EventHandlerList eventHandlerList_obj = (EventHandlerList)propEvents.GetValue(obj, null);

    eventHandlerList_obj.AddHandlers(eventos);
}

Questo è sicuramente più pulito della risposta precedente. Fa esattamente la stessa cosa? Sembra che lo sia, ma forse mi manca qualcosa. Inoltre, perché è necessario creare un nuovo oggetto quando tutto ciò che si desidera è un EventHandlerList? Non esiste un c-tor accessibile per EventHandlerList, in modo tale da poterne ottenere solo uno costruito internamente per un componente?
Michael,

1

Questa pagina mi ha aiutato molto. Il codice che ho ricevuto da qui aveva lo scopo di rimuovere un evento clic da un pulsante. Devo rimuovere gli eventi doppio clic da alcuni pannelli e gli eventi clic da alcuni pulsanti. Quindi ho creato un'estensione di controllo, che rimuoverà tutti i gestori di eventi per un determinato evento.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
public static class EventExtension
{
    public static void RemoveEvents<T>(this T target, string eventName) where T:Control
    {
        if (ReferenceEquals(target, null)) throw new NullReferenceException("Argument \"target\" may not be null.");
        FieldInfo fieldInfo = typeof(Control).GetField(eventName, BindingFlags.Static | BindingFlags.NonPublic);
        if (ReferenceEquals(fieldInfo, null)) throw new ArgumentException(
            string.Concat("The control ", typeof(T).Name, " does not have a property with the name \"", eventName, "\""), nameof(eventName));
        object eventInstance = fieldInfo.GetValue(target);
        PropertyInfo propInfo = typeof(T).GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
        EventHandlerList list = (EventHandlerList)propInfo.GetValue(target, null);
        list.RemoveHandler(eventInstance, list[eventInstance]);
    }
}

Ora, l'uso di questa estensione. Se devi rimuovere gli eventi clic da un pulsante,

Button button = new Button();
button.RemoveEvents(nameof(button.EventClick));

Se devi rimuovere gli eventi di doppio clic da un pannello,

Panel panel = new Panel();
panel.RemoveEvents(nameof(panel.EventDoubleClick));

Non sono un esperto di C #, quindi se ci sono dei bug per favore perdonami e fammelo sapere.


1
Il metodo di estensione .CastTo <> () dove si trova esattamente?
IbrarMumtaz,

Potresti semplicemente scrivere il tuo: public static T CastTo <T> (this object objectToCast) {return (T) objectToCast; }
KingOfHypocrites

0

A volte dobbiamo lavorare con i controlli di ThirdParty e dobbiamo creare queste soluzioni scomode. Con la risposta @Anoop muraleedharan ho creato questa soluzione con il tipo di inferenza e il supporto ToolStripItem

    public static void RemoveItemEvents<T>(this T target, string eventName) 
        where T : ToolStripItem
    {            
        RemoveObjectEvents<T>(target, eventName);
    }

    public static void RemoveControlEvents<T>(this T target, string eventName)
        where T : Control
    {
        RemoveObjectEvents<T>(target, eventName);
    }

    private static void RemoveObjectEvents<T>(T target, string Event) where T : class
    {
        var typeOfT = typeof(T);
        var fieldInfo = typeOfT.BaseType.GetField(
            Event, BindingFlags.Static | BindingFlags.NonPublic);
        var provertyValue = fieldInfo.GetValue(target);
        var propertyInfo = typeOfT.GetProperty(
            "Events", BindingFlags.NonPublic | BindingFlags.Instance);
        var eventHandlerList = (EventHandlerList)propertyInfo.GetValue(target, null);
        eventHandlerList.RemoveHandler(provertyValue, eventHandlerList[provertyValue]);
    }

E puoi usarlo in questo modo

    var toolStripButton = new ToolStripButton();
    toolStripButton.RemoveItemEvents("EventClick");

    var button = new Button();
    button.RemoveControlEvents("EventClick");

0

Ho trovato un'altra soluzione funzionante di Douglas .

Questo metodo rimuove tutti i gestori di eventi impostati su un evento di routet specifico su un elemento.
Usalo come

Remove_RoutedEventHandlers(myImage, Image.MouseLeftButtonDownEvent);

Codice completo:

/// <summary>
/// Removes all event handlers subscribed to the specified routed event from the specified element.
/// </summary>
/// <param name="element">The UI element on which the routed event is defined.</param>
/// <param name="RoutetEvent_ToRemove">The routed event for which to remove the event handlers.</param>
public static void RemoveRoutedEventHandlers(UIElement UIElement_Target, RoutedEvent RoutetEvent_ToRemove)
{
    // Get the EventHandlersStore instance which holds event handlers for the specified element.
    // The EventHandlersStore class is declared as internal.
    PropertyInfo PropertyInfo_EventHandlersStore = typeof(UIElement).GetProperty(
        "EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
    object oEventHandlersStore = PropertyInfo_EventHandlersStore.GetValue(UIElement_Target, null);

    // If there's no event handler subscribed, return
    if (oEventHandlersStore == null) return;

    // Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance 
    // for getting an array of the subscribed event handlers.
    MethodInfo MethodInfo_RoutedEventHandlers = oEventHandlersStore.GetType().GetMethod(
        "GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    RoutedEventHandlerInfo[] RoutedEventHandlerInfos = (RoutedEventHandlerInfo[])MethodInfo_RoutedEventHandlers.Invoke(
        oEventHandlersStore, new object[] { RoutetEvent_ToRemove });

    // Iteratively remove all routed event handlers from the element.
    foreach (RoutedEventHandlerInfo RoutedEventHandlerInfo_Tmp in RoutedEventHandlerInfos)
        UIElement_Target.RemoveHandler(RoutetEvent_ToRemove, RoutedEventHandlerInfo_Tmp.Handler);
}

0

rimuove tutti i gestori per button: save.RemoveEvents ();

public static class EventExtension
{
    public static void RemoveEvents<T>(this T target) where T : Control
    {
       var propInfo = typeof(T).GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
        var list = (EventHandlerList)propInfo.GetValue(target, null);
        list.Dispose();
    }
}

-1

Bene, qui c'è un'altra soluzione per rimuovere un evento associato (se hai già un metodo per gestire gli eventi per il controllo):

EventDescriptor ed = TypeDescriptor.GetEvents(this.button1).Find("MouseDown",true);            
Delegate delegate = Delegate.CreateDelegate(typeof(EventHandler), this, "button1_MouseDownClicked");
if(ed!=null) 
    ed.RemoveEventHandler(this.button1, delegate);

Puoi semplicemente fare questo.button1.MouseDown - = Delegate.CreateDelegate (typeof (EventHandler), questo, "button1_MouseDownClicked"). Quindi non aiuterà a risolvere la domanda su come scoprire quale delegato rimuovere, soprattutto se erano in linea.
Softlion,

-1

Questa non è una risposta all'OP, ma ho pensato di pubblicarla qui nel caso in cui potesse aiutare gli altri.

  /// <summary>
  /// Method to remove a (single) SocketAsyncEventArgs.Completed event handler. This is 
  /// partially based on information found here: http://stackoverflow.com/a/91853/253938
  /// 
  /// But note that this may not be a good idea, being very .Net implementation-dependent. Note 
  /// in particular use of "m_Completed" instead of "Completed".
  /// </summary>
  private static void RemoveCompletedEventHandler(SocketAsyncEventArgs eventArgs)
  {
     FieldInfo fieldInfo = typeof(SocketAsyncEventArgs).GetField("m_Completed", 
                                                BindingFlags.Instance | BindingFlags.NonPublic);
     eventArgs.Completed -= (EventHandler<SocketAsyncEventArgs>)fieldInfo.GetValue(eventArgs);
  }

-3

Ho trovato questa risposta e si adattava quasi alle mie esigenze. Grazie a SwDevMan81 per la classe. L'ho modificato per consentire la soppressione e la ripresa dei singoli metodi e ho pensato di pubblicarlo qui.

// This class allows you to selectively suppress event handlers for controls.  You instantiate
// the suppressor object with the control, and after that you can use it to suppress all events
// or a single event.  If you try to suppress an event which has already been suppressed
// it will be ignored.  Same with resuming; you can resume all events which were suppressed,
// or a single one.  If you try to resume an un-suppressed event handler, it will be ignored.

//cEventSuppressor _supButton1 = null;
//private cEventSuppressor SupButton1 {
//    get {
//        if (_supButton1 == null) {
//            _supButton1 = new cEventSuppressor(this.button1);
//        }
//        return _supButton1;
//    }
//}
//private void button1_Click(object sender, EventArgs e) {
//    MessageBox.Show("Clicked!");
//}

//private void button2_Click(object sender, EventArgs e) {
//    SupButton1.Suppress("button1_Click");
//}

//private void button3_Click(object sender, EventArgs e) {
//    SupButton1.Resume("button1_Click");
//}
using System;
using System.Collections.Generic;
using System.Text;

using System.Reflection;
using System.Windows.Forms;
using System.ComponentModel;

namespace Crystal.Utilities {
    public class cEventSuppressor {
        Control _source;
        EventHandlerList _sourceEventHandlerList;
        FieldInfo _headFI;
        Dictionary<object, Delegate[]> suppressedHandlers = new Dictionary<object, Delegate[]>();
        PropertyInfo _sourceEventsInfo;
        Type _eventHandlerListType;
        Type _sourceType;

        public cEventSuppressor(Control control) {
            if (control == null)
                throw new ArgumentNullException("control", "An instance of a control must be provided.");

            _source = control;
            _sourceType = _source.GetType();
            _sourceEventsInfo = _sourceType.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
            _sourceEventHandlerList = (EventHandlerList)_sourceEventsInfo.GetValue(_source, null);
            _eventHandlerListType = _sourceEventHandlerList.GetType();
            _headFI = _eventHandlerListType.GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
        }
        private Dictionary<object, Delegate[]> BuildList() {
            Dictionary<object, Delegate[]> retval = new Dictionary<object, Delegate[]>();
            object head = _headFI.GetValue(_sourceEventHandlerList);
            if (head != null) {
                Type listEntryType = head.GetType();
                FieldInfo delegateFI = listEntryType.GetField("handler", BindingFlags.Instance | BindingFlags.NonPublic);
                FieldInfo keyFI = listEntryType.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic);
                FieldInfo nextFI = listEntryType.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic);
                retval = BuildListWalk(retval, head, delegateFI, keyFI, nextFI);
            }
            return retval;
        }

        private Dictionary<object, Delegate[]> BuildListWalk(Dictionary<object, Delegate[]> dict,
                                    object entry, FieldInfo delegateFI, FieldInfo keyFI, FieldInfo nextFI) {
            if (entry != null) {
                Delegate dele = (Delegate)delegateFI.GetValue(entry);
                object key = keyFI.GetValue(entry);
                object next = nextFI.GetValue(entry);

                if (dele != null) {
                    Delegate[] listeners = dele.GetInvocationList();
                    if (listeners != null && listeners.Length > 0) {
                        dict.Add(key, listeners);
                    }
                }
                if (next != null) {
                    dict = BuildListWalk(dict, next, delegateFI, keyFI, nextFI);
                }
            }
            return dict;
        }
        public void Resume() {
        }
        public void Resume(string pMethodName) {
            //if (_handlers == null)
            //    throw new ApplicationException("Events have not been suppressed.");
            Dictionary<object, Delegate[]> toRemove = new Dictionary<object, Delegate[]>();

            // goes through all handlers which have been suppressed.  If we are resuming,
            // all handlers, or if we find the matching handler, add it back to the
            // control's event handlers
            foreach (KeyValuePair<object, Delegate[]> pair in suppressedHandlers) {

                for (int x = 0; x < pair.Value.Length; x++) {

                    string methodName = pair.Value[x].Method.Name;
                    if (pMethodName == null || methodName.Equals(pMethodName)) {
                        _sourceEventHandlerList.AddHandler(pair.Key, pair.Value[x]);
                        toRemove.Add(pair.Key, pair.Value);
                    }
                }
            }
            // remove all un-suppressed handlers from the list of suppressed handlers
            foreach (KeyValuePair<object, Delegate[]> pair in toRemove) {
                for (int x = 0; x < pair.Value.Length; x++) {
                    suppressedHandlers.Remove(pair.Key);
                }
            }
            //_handlers = null;
        }
        public void Suppress() {
            Suppress(null);
        }
        public void Suppress(string pMethodName) {
            //if (_handlers != null)
            //    throw new ApplicationException("Events are already being suppressed.");

            Dictionary<object, Delegate[]> dict = BuildList();

            foreach (KeyValuePair<object, Delegate[]> pair in dict) {
                for (int x = pair.Value.Length - 1; x >= 0; x--) {
                    //MethodInfo mi = pair.Value[x].Method;
                    //string s1 = mi.Name; // name of the method
                    //object o = pair.Value[x].Target;
                    // can use this to invoke method    pair.Value[x].DynamicInvoke
                    string methodName = pair.Value[x].Method.Name;

                    if (pMethodName == null || methodName.Equals(pMethodName)) {
                        _sourceEventHandlerList.RemoveHandler(pair.Key, pair.Value[x]);
                        suppressedHandlers.Add(pair.Key, pair.Value);
                    }
                }
            }
        }
    } 
}

8
Questa è una soluzione contorta e non dovrebbe mai essere utilizzata in software di livello industriale. L'approccio migliore è come detto: gestisci bene l'abbonamento all'evento e l'annullamento dell'iscrizione e non avrai mai problemi di questo tipo.
Tri Q Tran,

Sono d'accordo che non dovremmo usare la riflessione per sottrarre eventi, e l'applicazione e la disiscrizione dovrebbero essere gestite dall'applicazione. Penso che la questione in discussione dovrebbe essere usata al momento del DEBUG, per scoprire se stiamo disturbando qualcosa. Questo è un must per le applicazioni legacy che stai refactoring.
Tiago Freitas Leal,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.