Як видалити всі обробники подій із події


366

Щоб створити новий обробник подій на контролі, ви можете це зробити

c.Click += new EventHandler(mainFormButton_Click);

або це

c.Click += mainFormButton_Click;

і щоб видалити обробник подій, ви можете це зробити

c.Click -= mainFormButton_Click;

Але як ви видалити з події всі обробники подій?


10
Якщо хтось завітав сюди, шукаючи рішення WPF, ви можете поглянути на цю відповідь .
Дуглас

1
Ви не можете просто встановити c.Click = null?
алексанія

Це одна з тих речей, які я вважаю смішно надскладними. Простий Clearметод мав занадто багато зусиль, мабуть
Зімано

Відповіді:


167

Я знайшов рішення на форумах MSDN . Зразок наведеного нижче коду видалить усі Clickподії 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]);
        }
    }
}

Якщо для кнопки1 встановлено значення "Нуль", чи всі обробники подій приєднані до кнопки1.Клік натисніть правильно?
Демієн

3
Виправте мене, якщо я помиляюся, але чи не слід з першого рядка RemoveClickEventпочинати FieldInfo f1 = typeof(Button):? Я отримую нульове значення, GetFieldякщо використовую Control.
Захисник один

2
Схоже, це не працює для ToolStripButtons. Я замінив кнопку в RemoveClickEvent на ToolStripButton, але події все ще мають місце після виклику RemoveClickEvent. Хтось вирішив цю проблему?
Скаллі

1
вищезазначене посилання в MSDN також пропонує спробувати myButton.Click + = null; якщо ви хочете видалити всіх делегатів (не для Клацання, а для інших подій ..)
hello_earth

1
@hello_earth Схоже, не працюєObservableCollection.CollectionChanged += null;
Майк де Клерк

146

Ви, хлопці, надто важко ставитеся до себе. Це так просто:

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

57
Це працює лише в тому випадку, якщо ви є власником події. Спробуйте це зробити на контролі.
Делян

226
... і якщо ви є власником події, ви можете просто написати, FindClicked = null;що є досить простим.
Джон Скіт

79
Що таке FindClicked?
Левитикон

3
Це не працює для подій Kinect - kinect.ColorFrameReady -= MyEventHanderтак, але не існує GetInvocationList()методу в екземплярах kinect для повторення своїх делегатів.
Brent Faust

GetInvocationListне знайдено.
Жарт Хуан

75

З видалення всіх обробників подій :

Прямо ні, значною мірою тому, що ви не можете просто встановити подію на нуль.

Побічно, ви можете зробити фактичну подію приватною та створити навколо неї властивість, яка відстежує всіх доданих / віднятих до неї делегатів.

Візьміть наступне:

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
Я думав, що ОП посилається на загальні .net управління .., в яких подібне обгортання може бути неможливим.
Gishu

4
ви могли б отримати контроль, тоді це було б
Том Фобеар,

Це також призводить до підтримки двох списків, див. Stackoverflow.com/questions/91778/… для скидання або stackoverflow.com/questions/91778/… для доступу до списку.
ТН.

63

Прийнята відповідь не повна. Він не працює для подій, оголошених як {add; видалити;}

Ось робочий код:

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
Ця версія працювала для мене. Прийнята версія не працювала. +1 для цього.
Мейстер Шніцель

1
Не працював для WPF подій, поки я не використовував BindingFlags.Publicперший GetFieldдзвінок.
Леннарт

40

Видалити неіснуючий обробник подій не шкодить. Тож якщо ви знаєте, які там можуть бути обробники, їх можна просто видалити. У мене просто був подібний випадок. Це може допомогти в деяких випадках.

Подібно до:

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

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

16

Я фактично використовую цей метод, і він працює чудово. Мене "надихнуло" код, написаний тут Aeonhack .

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

Поле MyEventEvent приховано, але воно існує.

Налагоджуючи, ви можете бачити, як d.targetоб’єкт насправді обробляє подію, іd.method її метод. Вам потрібно лише видалити його.

Це чудово працює. Більше об'єктів не буде GC'ed через обробників подій.


2
Будь ласка, не пишіть відповіді іншими мовами.
Хілл

10

Я ненавидів будь-які показані тут цілісні рішення, я робив суміш і тестував зараз, працював на будь-якому обробці подій:

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;
        }
    }
}

Легко! Дякую за Стівена Пунака.

Я використовував це, тому що я використовую загальний локальний метод для видалення делегатів, і локальний метод був викликаний після різних випадків, коли встановлюються різні делегати.


4

Якщо вам справді доведеться це зробити ... це займе роздуми і ще досить часу, щоб це зробити. Обробниками подій керує карта-подія-делегат всередині елемента управління. Вам потрібно було б

  • Відбийте та отримайте цю карту в контрольному екземплярі.
  • Повторіть кожну подію, знайдіть делегата
    • кожен делегат, в свою чергу, може бути ланцюговою серією обробників подій. Тому дзвоніть на obControl.RemoveHandler (подія, обробник)

Словом, багато роботи. Теоретично це можливо ... Я ніколи не пробував щось подібне.

Подивіться, чи можете ви мати кращий контроль / дисципліну протягом фази підписатися-скасувати підписку на контроль.


3

Стівен має право. Це дуже просто:

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
Боже, компілятор повинен заборонити такі імена змінних. graphs_must_be_redrawn французькою мовою.
gracchus

4
Переклад з французької foreach (EventHandler<MyCompletedArgs> handler in CompletionCompleted.GetInvocationList()) { CompletionCompleted -= handler; }
Антон К

Переклад англійською мовою @AntonK працює добре. Не забудьте перевірити наявність нуля на обробці ресурсу.
Бретт

2

Щойно я дізнався Як призупинити події під час встановлення властивості елемента управління WinForms . Це видалить усі події з елемента контролю:

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
Це було дуже корисно, але є одне, що потрібно змінити: В Resume () ви додаєте обробники назад у зворотному порядку (я припускаю, що це копія / вставка з Suppress, де ви хочете працювати назад, тому як не возитися з колекцією, яку ви повторюєте). Деякий код розраховує на те, що обробники стріляють у визначеному порядку, тому з цим не слід возитися.
Михайло

1

Ого. Я знайшов це рішення, але нічого не працювало так, як я хотів. Але це так добре:

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);
}

Це, безумовно, акуратніше попередньої відповіді. Чи робить це саме те саме? Схоже, це і є, але, можливо, мені чогось не вистачає. Крім того, навіщо вам створювати новий об’єкт, коли все, що вам потрібно, це EventHandlerList? Чи не доступний c-tor для EventHandlerList, такий, що можна отримати лише той, який був побудований внутрішньо для компонента?
Майкл

1

Ця сторінка мені дуже допомогла. Код, який я взяв звідси, мав на меті видалити подію натискання кнопки. Мені потрібно видалити події подвійного клацання з деяких панелей і натиснути події з деяких кнопок. Тому я зробив розширення управління, яке видалить усіх обробників подій для певної події.

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]);
    }
}

Тепер, використання цього розширення. Якщо вам потрібно видалити події клацання з кнопки,

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

Якщо вам потрібно видалити події подвійного клацання з панелі,

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

Я не є експертом у C #, тому якщо є якісь помилки, будь ласка, вибачте мене і будь ласка, повідомте про це.


1
Метод розширення .CastTo <> (), де саме це знайдено?
IbrarMumtaz

Ви можете просто написати свій: public static T CastTo <T> (цей об'єкт objectToCast) {return (T) objectToCast; }
KingOfHypocrites

0

Іноді нам доводиться працювати з елементами ThirdParty, і нам потрібно будувати ці незручні рішення. На основі відповіді @Anoop Muraleedharan я створив це рішення з типом висновку та підтримкою 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]);
    }

І ви можете використовувати це так

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

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

0

Я знайшов ще одне робоче рішення Дугласа .

Цей метод видаляє всі обробники подій, які встановлені на конкретній події маршруту на елементі.
Використовуйте це як

Remove_RoutedEventHandlers(myImage, Image.MouseLeftButtonDownEvent);

Повний код:

/// <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

видаляє всі обробники кнопки: 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

Ну, тут є ще одне рішення для видалення асоційованої події (якщо у вас вже є метод обробки подій для управління):

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);

Ви можете просто зробити це.button1.MouseDown - = Delegate.CreateDelegate (typeof (EventHandler), це, "button1_MouseDownClicked"). Таким чином, це не допоможе вирішити питання, яке полягає в тому, як дізнатися, якого делегата видалити, особливо якщо вони були вбудованими.
Softlion

-1

Це не відповідь на ОП, але я подумав, що опублікую це тут, якщо це може допомогти іншим.

  /// <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

Я знайшов цю відповідь, і це майже відповідало моїм потребам. Дякуємо SwDevMan81 за клас. Я змінив його, щоб дозволити придушення та відновлення окремих методів, і думав, що опублікую його тут.

// 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
Це складне рішення і його ніколи не слід використовувати в програмному забезпеченні промислового рівня. Найкращий підхід - це, як згадувалося: добре керуйте підпискою та скасування підписки на події, і ви ніколи не зіткнетеся з такими проблемами.
Три Q Тран

Я погоджуюсь, що ми не повинні використовувати роздуми для відключення подій, а передплатою та скасуванням підписки слід керувати додатком. Я думаю, що питання в дискусіях слід використовувати в DEBUG час, щоб з'ясувати, наскільки ми щось упускаємо. Це обов'язково для застарілих програм, які ви рефакторинг.
Тіаго Фрейтас Ліал
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.