使用lambda表达式作为事件处理程序的最佳实践

12

发现了lambda表达式及其作为匿名函数的用法后,我发现自己写了很多更琐碎的事件,比如这些:

txtLogin.GotFocus += (o, e) =>
{
    txtLogin.Text = string.Empty;
    txtLogin.ForeColor = SystemColors.ControlText;
};
txtLogin.LostFocus += (o, e) =>
{
    txtLogin.Text = "Login...";
    txtLogin.ForeColor = SystemColors.InactiveCaptionText;
};

我也已经放弃了只调用其他函数的事件处理程序,取而代之的是使用执行相同操作的小型 Lambda 表达式:

backgroundWorker.DoWork += (o, e) => DatabaseLookup.Open(e.Argument as string);

我发现一些类似的问题讨论性能问题并指出你不能删除它们,但我没有找到任何一个讨论简单问题的答案这是一个好主意吗?

在这种情况下使用lambda表达式被认为是良好的形式吗?或者更有经验的程序员是否看不起它?它是否隐藏了难以找到的事件处理程序,还是通过减少琐碎的事件处理程序对代码提供了服务?

3个回答

16

这是一个完全合理的想法 - 但在这种特定情况下,我会改用匿名方法:

txtLogin.LostFocus += delegate
{
    txtLogin.Text = "Login...";
    txtLogin.ForeColor = SystemColors.InactiveCaptionText;
};

匿名方法的好处是您不必指定参数-这使得清楚表明您没有使用它们。这是匿名方法仅有的优点,不能与Lambda表达式相提并论。

性能影响几乎总是可以忽略不计的。无法在之后删除它们是一个非常真实的问题,如果您确实需要能够删除处理程序,但我发现我经常不需要。 (Reactive Extensions 对此有一个很好的解决方案-当您订阅可观察序列时,您会收到一个 IDisposable ,如果您调用它,将删除订阅。非常整洁。)


有什么好的例子说明"你确实需要删除处理程序(handler)"吗?如果使用lambda或匿名方法的方法被多次调用会发生什么? - Daemon Painter
1
@DaemonPainter:任何时候,如果你想添加一个处理程序,然后稍后再删除它,例如,如果你想在操作期间仅订阅按钮单击事件,然后取消订阅。(这是一种更容易在其中识别的情况,而不必描述整个应用程序的具体示例。)我不确定你在第二条评论中所说的意思-我怀疑答案取决于确切的代码。如果你感兴趣,可以考虑在一个新问题中提问。 - Jon Skeet

1

实际上,将事件处理程序放置在易于查找的位置中,即紧挨着其分配给的事件名称旁边,这是一个考虑因素。

很多时候,您会看到像这样的事件处理程序:

 void Text1_KeyDown(....) {....}

附加到 txtFirstName 的 KeyUp 事件上,因为在使用智能感知创建处理程序后,有人决定重命名文本框,并且 KeyUp 功能更好。 使用 Lambda,对象、事件和函数都在一起。


-1

这是一个棘手的问题。我记得在Code Complete中读到过,一些(聪明的)人说,你应该尽可能保持控制流程简单,许多人主张从方法中单一进入和退出,因为不这样做会使程序更难以理解。

Lambdas甚至更远离这一点,在某些情况下非常难以理解正在发生什么,控制从一个地方跳到另一个地方。

基本上,我认为这可能是一个坏主意,因为这样做很有力量,使生活更轻松。我肯定会经常使用它们。 总之,要谨慎使用!


7
仅限于一个出口点会导致可读性灾难,我认为这是不可取的。如果我在一行代码后就知道了方法的结果(例如因为它是一个特殊情况),那么没有理由让读者跟随整个方法直到达到出口点。 - Jon Skeet
我同意,我总是更喜欢早点退出。但我知道有些人反对这种做法。 - Grant Crofton
我的理念是,通常最好避免在具有副作用的第一条语句和最后一条这样的语句之间退出函数,除非在 try 块内返回值比在块内设置变量并在外部返回它更加清晰。 - supercat

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