添加和删​​除匿名事件处理程序

时间:2010-01-12 18:27:12

标签: c# events anonymous-methods

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

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

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

6 个答案:

答案 0 :(得分:59)

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

How to Subscribe to and Unsubscribe from Events

特别注意:

  

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

还有:

  

重要的是要注意到你   不能轻易取消订阅   如果您使用匿名活动   订阅它的功能。至   取消订阅在这种情况下,它是   必须回到代码所在的地方   你订阅了这个活动,存储了   委托中的匿名方法   变量,然后将委托添加到   事件 。一般来说,我们建议   你不使用匿名   函数来订阅事件if   你必须取消订阅   在你的某个晚些时候的事件   代码。

答案 1 :(得分:12)

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

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

答案 2 :(得分:6)

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

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

答案 3 :(得分:3)

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

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

答案 4 :(得分:2)

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

答案 5 :(得分:1)

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