如何移除Lambda事件处理程序

265

我最近发现可以使用lambda表达式创建简单的事件处理程序。例如,我可以像这样订阅点击事件:

button.Click += (s, e) => MessageBox.Show("Woho");

但是你如何取消订阅它?


1
请查看这里:https://dev59.com/cnVC5IYBdhLWcg3wykaj - Martin Harris
2
@Svish:Lambda本质上是一个匿名方法。 - dtb
1
啊哈,那就是肯定的了。 - Svish
2
除非我漏掉了一些微妙的差别,否则你的问题已经在这里得到了回答:https://dev59.com/FnRA5IYBdhLWcg3w1BuW,尽管它的被接受的答案是错误的(但在评论中已经得到了纠正)。 - Jeff Sternal
1
是的,我现在已经找到至少3个类似的问题了,但这一个得到了非常好的答案。 - Svish
显示剩余3条评论
1个回答

386

C#规范明确指出(如果我没记错的话),如果你有两个匿名函数(匿名方法或Lambda表达式),它可能会从该代码创建相等的委托,也可能不会。 (如果两个委托具有相等的目标并引用相同的方法,则它们是相等的。)

要确保,您需要记住使用的委托实例:

EventHandler handler = (s, e) => MessageBox.Show("Woho");

button.Click += handler;
...
button.Click -= handler;

(我找不到相关规范的部分,但如果C#编译器试图积极创建相等委托,我会感到非常惊讶。依赖它肯定是不明智的。)

如果您不想那样做,您需要提取一个方法:

public void ShowWoho(object sender, EventArgs e)
{
     MessageBox.Show("Woho");
}

...

button.Click += ShowWoho;
...
button.Click -= ShowWoho;
如果你想创建一个使用lambda表达式自我删除的事件处理程序,那么稍微有些棘手-你需要在lambda表达式本身内引用委托,并且你不能通过简单地“声明一个局部变量并使用lambda表达式分配给它”的方式来实现,因为此时该变量并未明确定义。通常情况下,你可以通过首先将变量赋值为空来解决这个问题:
EventHandler handler = null;
handler = (sender, args) =>
{
    button.Click -= handler; // Unsubscribe
    // Add your one-time-only code here
}
button.Click += handler;

很不幸,将此封装为方法甚至都不容易,因为事件没有被清晰地表示出来。你可能能够接近的是类似于以下的东西:

button.Click += Delegates.AutoUnsubscribe<EventHandler>((sender, args) =>
{
    // One-time code here
}, handler => button.Click -= handler);

即使在Delegates.AutoUnsubscribe内部实现这个也会很棘手,因为你需要创建一个新的EventHandler(它只是一个泛型类型参数)。这是可行的,但会变得混乱。


2
确切地说,如果你使用 Reflector 查看编译后的程序集,你会注意到当你使用 lambda 时,编译器已经为你创建了一个指针,只是在 Visual Studio 中你看不到它。 - Raffaeu
2
@Raffaeu:称其为指针有点误导——它不是普通C#中指针的意义。 - Jon Skeet
4
是的,道歉。"编译器仍会创建处理程序变量" 这样表达更好吗? :) - Raffaeu
2
@Raffaeu:可能吧 :) - Jon Skeet
2
@JonSkeet 是的,确实不是分离它,而只是模拟它,因为我在每次按钮点击时都会创建一个新的后台工作对象。所以,正如你指出的那样,lambda 分离不起作用。感谢你纠正我 :) - dynamiclynk
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接