如何从事件中删除所有事件处理程序

Posted

技术标签:

【中文标题】如何从事件中删除所有事件处理程序【英文标题】:How to remove all event handlers from an event 【发布时间】:2010-09-10 15:27:56 【问题描述】:

要在控件上创建新的事件处理程序,您可以这样做

c.Click += new EventHandler(mainFormButton_Click);

或者这个

c.Click += mainFormButton_Click;

要删除事件处理程序,您可以这样做

c.Click -= mainFormButton_Click;

但是?

【问题讨论】:

如果有人来这里寻找 WPF 解决方案,您可能需要查看this answer。 你不能只设置c.Click = null吗? 这是我觉得过于复杂的事情之一。一个简单的Clear 方法显然太费劲了 在 .NET 4.5 中,如果 List.Count > 2 可以说,并且您尝试删除第一个委托 InvocatorList[0] == mainFormButton_Click... 就像您所做的那样......它将删除所有其中。我认为这是一个错误! 【参考方案1】:

我在MSDN forums 上找到了解决方案。下面的示例代码将从button1 中删除所有Click 事件。

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

【讨论】:

如果 button1 设置为 null,是否所有附加到 button1.Click 的事件处理程序都正确处理? 如果我错了,请纠正我,但RemoveClickEvent 的第一行不应该以:FieldInfo f1 = typeof(Button) 开头吗?如果我使用Control,我会从GetField 得到空值。 这似乎不适用于 ToolStripButtons。我已将 RemoveClickEvent 中的 Button 替换为 ToolStripButton,但在调用 RemoveClickEvent 后事件仍然存在。有没有人解决这个问题? MSDN 中的上述链接也建议尝试 myButton.Click += null;如果要删除所有代表(不是针对 Click,而是针对其他事件..) @hello_earth 似乎不适用于ObservableCollection.CollectionChanged += null;【参考方案2】:

你们对自己太苛刻了。就这么简单:

void OnFormClosing(object sender, FormClosingEventArgs e)

    foreach(Delegate d in FindClicked.GetInvocationList())
    
        FindClicked -= (FindClickedHandler)d;
    

【讨论】:

这只有在您拥有该事件时才有效。尝试在控件上执行此操作。 ...如果您拥有该活动,您可以只写FindClicked = null;,这样比较简单。 这对 Kinect 事件不起作用 -- kinect.ColorFrameReady -= MyEventHander 可以,但在 kinect 实例上没有 GetInvocationList() 方法来迭代其委托。 GetInvocationList 未找到。 @Timo:对,它必须是那个确切的类。【参考方案3】:

来自Removing All Event Handlers

直接不,很大程度上是因为你 不能简单地将事件设置为 null。

间接地,你可以做实际的 事件私有并创建一个属性 在它周围追踪所有 代表被添加/减去 它。

采取以下措施:

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

【讨论】:

我认为 OP 指的是一般的 .net 控件。在这种情况下,这种包装可能是不可能的。 你可以派生控件,然后它会 这也导致维护两个列表,请参阅***.com/questions/91778/… 重置或***.com/questions/91778/… 访问列表。 遗憾的是,我不能将此行为包装在接口上,因为它不能有实例字段。 @Noman_1 - 当您想要一个字段时,wlith 接口解决方案是使用一对方法(getter/setter)。然后由每个类来实现该方法对。【参考方案4】:

接受的答案不完整。它不适用于声明为 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;

【讨论】:

这个版本对我有用。接受的版本不起作用。为此 +1。 直到我在第一个 GetField 调用中使用 BindingFlags.Public 之前,它不适用于 WPF 事件。【参考方案5】:

删除不存在的事件处理程序没有任何害处。因此,如果您知道可能有哪些处理程序,您可以简单地删除所有处理程序。我刚遇到类似的情况。这在某些情况下可能会有所帮助。

喜欢:

// Add handlers...
if (something)

    c.Click += DoesSomething;

else

    c.Click += DoesSomethingElse;


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

【讨论】:

是的,但问题是您可以多次执行 +=,然后无法知道有多少相同的事件附加到处理程序。所以你不知道有多少-=你需要一路清除。【参考方案6】:

我讨厌这里显示的任何完整解决方案,我现在做了混合和测试,适用于任何事件处理程序:

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;
        
    

简单!感谢斯蒂芬·普纳克。

我使用它是因为我使用通用的本地方法来删除委托,并且在设置不同的委托时,在不同的情况下调用本地方法。

【讨论】:

【参考方案7】:

我实际上正在使用这种方法,并且效果很好。我受到 Aeonhack here 编写的代码的“启发”。

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。

【讨论】:

请不要用其他语言写答案。 我不同意 - 这是一个 .NET 问题和问题。 VB.NET 与解决此问题的任何其他 .NET 语言一样有效。我也会包含一个 C# 示例,因为它更常见,但偶尔看到一些 vb.net 仍然很高兴!【参考方案8】:

如果您真的必须这样做……这需要反思和相当长的时间。事件处理程序在控件内的事件到委托映射中进行管理。你需要

在控件实例中反映并获取此映射。 迭代每个事件,获取委托 每个委托又可以是一系列链接的事件处理程序。所以调用 obControl.RemoveHandler(event, handler)

简而言之,工作量很大。理论上是可能的……我从来没有尝试过这样的事情。

看看您是否可以更好地控制/约束控件的订阅-取消订阅阶段。

【讨论】:

【参考方案9】:

斯蒂芬是对的。这很容易:

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;
        
    

【讨论】:

天哪,编译器应该禁止这种变量名。 graphs_must_be_redrawn 法语。 从法语翻译 foreach (EventHandler&lt;MyCompletedArgs&gt; handler in CompletionCompleted.GetInvocationList()) CompletionCompleted -= handler; @AntonK 的英文翻译效果很好。请记住在属性处理程序上检查 null。【参考方案10】:

我刚刚找到How to suspend events when setting a property of a WinForms control。它将从控件中删除所有事件:

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

    

【讨论】:

这很有帮助,但有一点需要更改:在 Resume() 中,您正在以相反的顺序添加处理程序(我假设它是来自 Suppress 的复制/粘贴,你想向后工作,以免弄乱你正在迭代的集合)。一些代码依赖于按给定顺序触发的处理程序,所以不应该乱用它。【参考方案11】:

哇。我找到了这个解决方案,但没有像我想要的那样工作。但这太好了:

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 时,为什么还要创建一个新对象? EventHandlerList 是否没有可访问的 c-tor,因此只能获取为 Component 内部构造的 c-tor?【参考方案12】:

这个页面对我帮助很大。我从这里获得的代码旨在从按钮中删除单击事件。我需要从某些面板中删除双击事件并从某些按钮中删除单击事件。所以我做了一个控制扩展,它将删除某个事件的所有事件处理程序。

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# 方面的专家,所以如果有任何错误,请原谅我并请告诉我。

【讨论】:

.CastTo() 扩展方法到底在哪里找到的? 你可以自己写: public static T CastTo(this object objectToCast) return (T)objectToCast; 【参考方案13】:

聚会有点晚了,但我使用了这个对我来说效果很好的链接: https://www.codeproject.com/Articles/103542/Removing-Event-Handlers-using-Reflection

此代码的美妙之处在于它适用于所有人,WFP、Forms、Xamarin Forms。我将它用于 Xamarin。请注意,仅当您不拥有此事件时才需要这种使用反射的方式(例如,在您不关心的某些事件上崩溃的库代码)。

这是我稍作修改的代码:

    static Dictionary<Type, List<FieldInfo>> dicEventFieldInfos = new Dictionary<Type, List<FieldInfo>>();

    static BindingFlags AllBindings
    
        get  return BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; 
    

    static void BuildEventFields(Type t, List<FieldInfo> lst)
    
        foreach (EventInfo ei in t.GetEvents(AllBindings))
        
            Type dt = ei.DeclaringType;
            FieldInfo fi = dt.GetField(ei.Name, AllBindings);
            if (fi != null)
                lst.Add(fi);
        
    
    static List<FieldInfo> GetTypeEventFields(Type t)
    
        if (dicEventFieldInfos.ContainsKey(t))
            return dicEventFieldInfos[t];

        List<FieldInfo> lst = new List<FieldInfo>();
        BuildEventFields(t, lst);
        dicEventFieldInfos.Add(t, lst);
        return lst;
    
    static EventHandlerList GetStaticEventHandlerList(Type t, object obj)
    
        MethodInfo mi = t.GetMethod("get_Events", AllBindings);
        return (EventHandlerList)mi.Invoke(obj, new object[]  );
    
    public static void RemoveEventHandler(object obj, string EventName = "")
    
        if (obj == null)
            return;

        Type t = obj.GetType();
        List<FieldInfo> event_fields = GetTypeEventFields(t);
        EventHandlerList static_event_handlers = null;

        foreach (FieldInfo fi in event_fields)
        
            if (EventName != "" && string.Compare(EventName, fi.Name, true) != 0)
                continue;
            var eventName = fi.Name;
            // After hours and hours of research and trial and error, it turns out that
            // STATIC Events have to be treated differently from INSTANCE Events...
            if (fi.IsStatic)
            
                // STATIC EVENT
                if (static_event_handlers == null)
                    static_event_handlers = GetStaticEventHandlerList(t, obj);

                object idx = fi.GetValue(obj);
                Delegate eh = static_event_handlers[idx];
                if (eh == null)
                    continue;

                Delegate[] dels = eh.GetInvocationList();
                if (dels == null)
                    continue;

                EventInfo ei = t.GetEvent(eventName, AllBindings);
                foreach (Delegate del in dels)
                    ei.RemoveEventHandler(obj, del);
            
            else
            
                // INSTANCE EVENT
                EventInfo ei = t.GetEvent(eventName, AllBindings);
                if (ei != null)
                
                    object val = fi.GetValue(obj);
                    Delegate mdel = (val as Delegate);
                    if (mdel != null)
                    
                        foreach (Delegate del in mdel.GetInvocationList())
                        
                            ei.RemoveEventHandler(obj, del);
                        
                    
                
            
        
    


示例用法:RemoveEventHandler(obj, "Focused");

【讨论】:

【参考方案14】:

有时我们必须使用第三方控件,并且我们需要构建这些笨拙的解决方案。基于@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");

【讨论】:

【参考方案15】:

删除按钮的所有处理程序: 保存.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();
    

【讨论】:

【参考方案16】:

好吧,这里有另一个解决方案来删除关联事件(如果您已经有处理控件事件的方法):

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), this, "button1_MouseDownClicked")。因此,它无助于解决如何找出要删除的委托的问题,尤其是如果它们是内联的。【参考方案17】:

这不是对 OP 的回答,但我想我会在这里发布它以防它可以帮助其他人。

  /// <summary>
  /// Method to remove a (single) SocketAsyncEventArgs.Completed event handler. This is 
  /// partially based on information found here: http://***.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);
  

【讨论】:

【参考方案18】:

我找到了这个答案,它几乎符合我的需求。感谢 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);
                    
                
            
        
     

【讨论】:

这是一个复杂的解决方案,绝不应该在工业级软件中使用。如前所述,最好的方法是:管理好您的活动订阅和取消订阅,您将永远不会遇到此类问题。 我同意我们不应该使用反射来取消连接事件,并且事件订阅和取消订阅应该由应用程序管理。我认为辩论中的问题应该在 DEBUG 时使用,以找出我们是否在做某事。对于您正在重构的遗留应用程序,这是必须的。

以上是关于如何从事件中删除所有事件处理程序的主要内容,如果未能解决你的问题,请参考以下文章

如何删除“按钮”的“单击”事件的所有事件处理程序?

使用单个事件处理程序 asp.net 从网格中处理多个删除事件

如何删除所有eventhandler

js如何清除所有绑定的事件?

删除特定控件的所有事件处理程序

是否可以在 javascript 中删除给定元素的所有事件处理程序?