如何删除lambda事件处理程序

Svi*_*ish 223 c# events lambda event-handling

可能重复:
在C#中取消订阅匿名方法
如何取消注册"匿名"事件处理程序

我最近发现我可以使用lambdas来创建简单的事件处理程序.我可以订阅像这样的点击事件:

button.Click += (s, e) => MessageBox.Show("Woho");
Run Code Online (Sandbox Code Playgroud)

但你怎么取消订阅呢?

Jon*_*eet 320

C#规范明确声明(IIRC)如果你有两个匿名函数(匿名方法或lambda表达式),它可能会也可能不会从该代码创建相等的委托.(如果两名代表有相同的目标并且参考相同的方法,则两名代表是平等的.)

当然,您需要记住您使用的委托实例:

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

button.Click += handler;
...
button.Click -= handler;
Run Code Online (Sandbox Code Playgroud)

(我找不到规范的相关部分,但是我很惊讶地看到C#编译器积极地尝试创建相同的委托.依赖它肯定是不明智的.)

如果您不想这样做,则需要提取方法:

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

...

button.Click += ShowWoho;
...
button.Click -= ShowWoho;
Run Code Online (Sandbox Code Playgroud)

如果你想创建一个使用lambda表达式删除自身的事件处理程序,它有点棘手 - 你需要在lambda表达式本身中引用委托,你不能用简单的"声明一个局部变量并分配使用lambda表达式"因为那时变量没有明确赋值.您通常首先通过为变量赋值空值来解决此问题:

EventHandler handler = null;
handler = (sender, args) =>
{
    button.Click -= handler; // Unsubscribe
    // Add your one-time-only code here
}
button.Click += handler;
Run Code Online (Sandbox Code Playgroud)

不幸的是,将它封装到方法中并不容易,因为事件没有清晰地表示.你最接近的将是:

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

即使这样做也很难实现,Delegates.AutoUnsubscribe因为你必须创建一个新的EventHandler(这只是一个泛型类型的参数).可行,但凌乱.

  • 确切地说,如果您使用 Reflector 查看编译后的程序集,您会发现编译器在您使用 lambda 时已经为您创建了一个指针,只是您在 Visual Studio 中看不到它 (2认同)
  • @Raffaeu:称其为指针有点误导 - 它不是正常 C# 意义上的指针。 (2认同)
  • 是道歉,"编译器无论如何都会创建处理程序变量"它更好吗?:) (2认同)
  • @Raffaeu:可能:) (2认同)
  • @DynamicLynk:我对查看代码非常感兴趣 - 我强烈*怀疑*它实际上并不像您想象的那样运行。你可以问一个新问题(“为什么这会起作用,而 Jon Skeet 声称它不应该......”)或者只是私信我。 (2认同)
  • @JonSkeet是的,实际上它并没有分离它只是模拟它,因为我在我的示例中单击每个按钮时都创建了一个新的后台工作对象。所以 lambda 分离并不像你指出的那样工作。谢谢你让我直截了当:) (2认同)