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

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

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

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

有一个MSDN页面,谈到这一点:

如何订阅和取消订阅活动

特别注意:

如果稍后您不必退订事件,则可以使用添加赋值运算符(+ =)将匿名方法附加到事件。

并且:

重要的是要注意,如果您使用匿名函数来订阅它,则不能轻易取消订阅。 要在这种情况下取​​消订阅,有必要回到订阅事件的代码,将匿名方法存储在委托variables中,然后将委托添加到事件中。 一般来说,我们build议您不要使用匿名函数来订阅事件,如果您稍后在代码中必须退订事件。

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

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

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

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

我不相信这会起作用。 如果您确实需要从事件注销,则必须指定一个显式事件处理程序,您可以稍后取消注册,而不是匿名代理。

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

这个问题的常见解决scheme(您需要删除处理程序)只是简单地将lambaexpression式重构为适当的方法。 另一种方法是为事件处理程序委托维护一个类variables,然后添加和删除这个variables,尽pipe我个人并不喜欢它。 (比创build一个普通的方法更麻烦,如果有的话)。

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

你可以使用这种结构:

  private void button_Click(object sender, RoutedEventArgs e) { RoutedEventHandler onclick; // subcribe b_second.Click += onclick = (ss, aa) => { /* Just do what you need */ }; // unsubcribe b_second.Click -= onclick; }