【问题标题】:Removing all event handlers in one go一次性删除所有事件处理程序
【发布时间】:2017-10-21 02:24:11
【问题描述】:

问题:我有一个包含对象列表的文档类。这些对象会引发SolutionExpiredDisplayExpired 等事件。文档需要对此做出响应。

文档有时可以交换对象,但一个对象绝不应该是多个文档的“一部分”。

我的文档类包含一堆用作事件处理程序的方法。每当一个对象进入文档时,我使用AddHandler 来设置事件,每当从文档中删除一个对象时,我使用RemoveHandler 来撤消损坏。但是,在某些情况下,很难确保正确执行所有步骤,因此我最终可能会遇到流氓事件处理程序。

长话短说;如何删除所有指向特定方法的处理程序?请注意,我没有潜在事件源的列表,这些可以存储在任何地方。

类似:

RemoveHandler *.SolutionExpired, AddressOf DefObj_SolutionExpired

【问题讨论】:

标签: vb.net events


【解决方案1】:

您可以使用Delegate.RemoveAll()。 (你感兴趣的部分在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);
}

您应该注意,它不会改变您传递给它的委托的内容,它会返回一个改变的委托。因此,您将无法更改从表单中拖放到表单上的按钮上的事件,因为button1.Click 只能使用+=-=,而不是=。这不会编译:

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

此外,请确保无论您在何处实施此功能,您都在注意潜在的竞争条件。如果您从另一个线程正在调用的事件中删除处理程序,您最终可能会遇到一些非常奇怪的行为!

【讨论】:

  • 我不明白这是如何接受的,因为 OP 的条件是需要从他没有私有级别访问权限的事件中删除处理程序。我一定是错过了什么!
  • 这也是 C#,而 OP 的问题是关于 vb.net。虽然两者有很多重叠,但事件语法是它们在方面有很大不同的地方
【解决方案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;
            }
        }
    }
}

编辑 2: 对我的误解深表歉意 - 我看到您很清楚无法访问原始帖子中的事件源。

我能想到的解决此问题的最简单方法是实现对象到文档绑定的共享字典。当一个对象进入一个文档时,检查字典中是否存在与另一个文档的绑定;如果存在,请删除引用旧文档的处理程序,然后再为新文档添加它们。无论哪种方式,都用新的绑定更新字典。

我认为在大多数情况下,性能和内存影响可以忽略不计:除非您处理数以万计的小对象并经常在文档之间交换它们,否则每个键/值对的内存开销和性能影响每个查找操作都应该很小。

作为替代方案:如果您可以(在文档事件处理程序中)检测到事件的发送者不再与文档相关,您可以在那里分离事件。

这些似乎是您可能已经拒绝的想法——但也许不是!

【讨论】:

  • 本,感谢您发布此信息。恐怕这对我(或就此而言的 VB 编译器)没有多大意义。能否请您发布原始 C#?
  • 我知道我应该保留原来的 C# 版本。 :-)
  • 谢谢 Ben,我知道你在这里做什么,但如果我没记错的话,这个函数假定我有一个对引发事件的实例的引用。这些物体可能早就消失在地平线上的某个地方的另一个列表中。我开始认为这可能是不可能的,因为处理程序委托是在引发事件的类中定义的,而不是在定义处理程序的类中。
【解决方案3】:

使用Delegate.RemoveAll(如果 Delegate 实例是私有的,可以使用反射)。

【讨论】:

  • 因为我需要有一个源实例。我认为...我不再有权访问事件引发对象,只能访问处理程序。
猜你喜欢
  • 2012-09-04
  • 1970-01-01
  • 2021-10-29
  • 1970-01-01
  • 2016-07-05
  • 2013-09-17
相关资源
最近更新 更多