60

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

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

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

4

6 回答 6

64

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

如何订阅和取消订阅事件

特别注意:

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

并且:

重要的是要注意,如果您使用匿名函数订阅某个事件,您将无法轻松取消订阅该事件。要在这种情况下取​​消订阅,需要返回订阅事件的代码,将匿名方法存储在委托变量中,然后将委托添加到事件中。通常,如果您必须在稍后的代码中取消订阅事件,我们建议您不要使用匿名函数来订阅事件。

于 2010-01-12T19:01:05.567 回答
22

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

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 中,我们支持本地函数,因此该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;
}
于 2015-06-10T17:45:30.870 回答
6

如果您需要取消订阅事件处理程序,则需要明确引用具体委托。仔细看Delegate.Equality你会发现委托不只是使用引用相等来比较,但这对于匿名委托无关紧要。

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

于 2010-01-12T18:37:54.890 回答
3

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

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

于 2010-01-12T18:37:23.210 回答
2

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

于 2010-01-12T18:32:40.157 回答
1

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

于 2010-01-12T18:31:48.870 回答