我想知道这是否真的有效?
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);
}
编译器如何知道事件处理程序是否相同?这甚至是推荐的吗?
答案 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的文档,您会发现它们没有通过引用进行比较。