【问题标题】:Adding and Removing Anonymous Event Handler添加和删​​除匿名事件处理程序
【发布时间】:2010-01-12 18:27:11
【问题描述】:

我想知道这是否真的有效?

private void RegisterKeyChanged(T item) 
{
    item.OnKeyChanged += (o, k) => ChangeItemKey((T)o, k);
}

private void UnRegisterKeyChanged(T item) 
{
    item.OnKeyChanged -= (o, k) => ChangeItemKey((T)o, k);
}

编译器如何知道事件处理程序是相同的?这甚至被推荐吗?

【问题讨论】:

标签: c# events anonymous-methods


【解决方案1】:

有一个 MSDN 页面讨论了这个:

How to Subscribe to and Unsubscribe from Events

特别注意:

如果您不必取消订阅 [原文如此] 之后的活动,您可以使用 加法赋值运算符 (+=) 到 将匿名方法附加到 事件。

还有:

请务必注意,您 不能轻易退订 如果您使用匿名的事件 订阅它的功能。到 在这种情况下取​​消订阅,它是 有必要回到代码所在的地方 您订阅事件,存储 委托中的匿名方法 变量,然后将委托添加到 事件 。一般来说,我们建议 你不使用匿名 订阅事件的函数,如果 您将不得不退订 在您稍后的某个时间点发生的事件 代码。

【讨论】:

  • 这是否意味着,基本上 OP 中的内容不能按预期工作?这是否意味着匿名委托,如果不存储在订阅点,几乎不可能取消订阅?
  • @Johnny_D 是的,OP 提供的代码不起作用。在新版本的 IDE 中,这会给你一个编译警告,说“通过匿名委托取消订阅事件”。当此代码执行时,它将给出一个运行时错误,即您尝试取消订阅的事件处理程序不存在
【解决方案2】:

对于任何感兴趣的人,您可以像这样添加和删除匿名事件处理程序

public class Musician
{
    public void TuneGuitar()
    {
        Metronome metronome = new Metronome();

        EventHandler<EventArgs> handler = null;
        handler = (sender, args) =>
        {
            // Tune guitar
            // ...

            // Unsubscribe from tick event when guitar sound is perfect
            metronome.Tick -= handler;
        };

        // Attach event handler
        metronome.Tick += handler;
    }
}

public class Metronome
{
    event EventHandler<EventArgs> Tick;
}

更新: 在 C# 7.0 中,我们支持 local functions,因此 TuneGuitar 方法现在可以写成:

public void TuneGuitar()
{
    Metronome metronome = new Metronome();

    void handler(object sender, EventArgs args)
    {
        // Tune guitar
        // ...

        // Unsubscribe from tick event when guitar sound is perfect
        metronome.Tick -= handler;
    };

    // Attach event handler
    metronome.Tick += handler;
}

【讨论】:

    【解决方案3】:

    如果您需要取消订阅事件处理程序,您需要明确引用具体委托。查看Delegate.Equality,您会发现委托不仅仅使用引用相等性进行比较,但这对于匿名委托无关紧要。

    对于匿名委托,编译器(基本上)只是为每个匿名委托创建一个新的“非匿名”委托,即使委托主体相同。因此,当您使用您提供的代码示例时,框架将找不到要取消订阅的委托。

    【讨论】:

      【解决方案4】:

      恐怕这行不通,因为您声明的两个 lambda 表达式(和委托)实际上是不同的对象,并返回不同的引用。因此,处理程序 (-=) 的删除将始终失败。

      这个问题的常见解决方案(您需要删除处理程序)只是将lamba表达式重构为适当的方法。另一种方法是为事件处理程序委托维护一个类变量,并添加和删除它,尽管我个人不喜欢它。 (如果有的话,这比创建一个普通的方法更麻烦。)

      【讨论】:

      • 注意,stackoverflow.com/questions/1348150/…> 有一个非常相似的问题和答案,Reed Copsey 提出了与我相同的解决方案。
      【解决方案5】:

      我不相信这会奏效。如果您确实需要从事件中注销,则必须指定一个显式事件处理程序,以后可以从该事件处理程序中注销,而不是匿名委托。

      【讨论】:

        【解决方案6】:

        如果您检查 Delegate.Equality 的文档,您会发现它们没有通过引用进行比较。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-09-21
          • 2012-05-10
          • 1970-01-01
          • 1970-01-01
          • 2016-06-06
          • 2010-11-26
          • 2012-04-28
          • 1970-01-01
          相关资源
          最近更新 更多