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

Car*_*ick 348 .net c# events

要在控件上创建新的事件处理程序,您可以执行此操作

c.Click += new EventHandler(mainFormButton_Click);
Run Code Online (Sandbox Code Playgroud)

或这个

c.Click += mainFormButton_Click;
Run Code Online (Sandbox Code Playgroud)

并删除事件处理程序,您可以执行此操作

c.Click -= mainFormButton_Click;
Run Code Online (Sandbox Code Playgroud)

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

xsl*_*xsl 164

我在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]);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果我错了,请纠正我,但不应该以'RemoveClickEvent`的第一行开头:`FieldInfo f1 = typeof(Button)`?如果我使用`Control`,我从`GetField`得到null. (3认同)
  • @hello_earth 似乎不适用于 `ObservableCollection.CollectionChanged += null;` (3认同)
  • 这似乎不适用于ToolStripButtons.我已经用ToolStripButton替换了RemoveClickEvent中的Button,但是在调用RemoveClickEvent之后事件仍然存在.有没有人解决这个问题? (2认同)

小智 138

你们这些方式对你们来说太过刻板了.就这么简单:

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

  • ...如果您拥有该事件,您可以编写`FindClicked = null;`这相当简单. (214认同)
  • 什么是FindClicked? (75认同)
  • 这只有在您拥有该活动时才有效.尝试在控件上执行此操作. (57认同)
  • 这不适用于Kinect事件 - "kinect.ColorFrameReady - = MyEventHander",但kinect实例上没有`GetInvocationList()`方法来迭代它们的代理. (2认同)

Jor*_*ira 73

删除所有事件处理程序:

直接不,很大程度上是因为你不能简单地将事件设置为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();
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为OP指的是一般的.net控件..其中这种包装可能是不可能的. (4认同)
  • 你可以导出控件,然后就可以了 (4认同)

Lio*_*oft 60

接受的答案不完整.它不适用于声明为{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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这个版本对我有用.接受的版本不起作用.为此+1. (3认同)
  • 直到我在第一个 `GetField` 调用中使用了 `BindingFlags.Public` 才对 WPF 事件起作用。 (2认同)

小智 39

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

喜欢:

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

// Remove handlers...
c.Click -= DoesSomething;
c.Click -= DoesSomethingElse;
Run Code Online (Sandbox Code Playgroud)

  • 是的,但问题是您可以多次执行 += 操作,然后无法知道有多少相同的事件附加到处理程序。所以你不知道需要多少个 -= 才能将其全部清除。 (4认同)

Iva*_*lla 17

我实际上正在使用这种方法,它完美无缺.我被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
Run Code Online (Sandbox Code Playgroud)

MyEventEvent字段是隐藏的,但确实存在.

调试时,您可以看到d.target对象实际上是如何处理事件的,以及d.method它的方法.你只需要删除它.

它很棒.由于事件处理程序,没有更多的对象没有GC.

  • 请不要用其他语言写答案。 (6认同)
  • 我不同意 - 这是一个 .NET 问题。VB.NET 与任何其他 .NET 语言一样可以有效解决此问题。我还会包含一个 C# 示例,因为它更常见,但偶尔看到一些 vb.net 仍然很高兴! (4认同)

小智 9

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

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

简单!谢谢Stephen Punak.

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

  • 根据 Jon Skeet 在其他地方的评论,如果您有“AnotherClass”的源代码,则清除方法可以是一行:“TheEventHandler = null;”。(如果 AnotherClass 不在您的代码中,那么不幸的是,不允许在该事件上调用“GetInitationList()”。) (6认同)

Gis*_*shu 5

如果你真的必须这样做......它需要反思并且需要相当长的时间才能做到这一点.事件处理程序在控件内的事件到委托映射中进行管理.你需要

  • 在控件实例中反映并获取此映射.
  • 迭代每个事件,获得代表
    • 每个代表反过来可以是一系列链式事件处理程序.所以调用obControl.RemoveHandler(event,handler)

总之,做了很多工作.这在理论上是可能的......我从来没有尝试过这样的事情.

看看你是否可以在控件的subscribe-unsubscribe阶段有更好的控制/纪律.