将 lambda 表达式用于事件处理程序的最佳实践
Posted
技术标签:
【中文标题】将 lambda 表达式用于事件处理程序的最佳实践【英文标题】:Best practices of using lambda expressions for event handlers 【发布时间】:2011-03-15 23:05:20 【问题描述】:在发现 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);
我发现一些类似的问题针对performance concerns 并指出您是can't remove them,但我没有找到任何针对这是个好主意吗?这个简单问题的解决方案p>
以这种方式使用 lambdas 是否被认为是好的形式,还是更有经验的程序员看不起这一点?它是把事件处理程序隐藏在难以找到的地方,还是通过减少琐碎的事件处理程序的数量来实现代码服务?
【问题讨论】:
【参考方案1】:这是一个完全合理的想法 - 但在这种特殊情况下,我会改用匿名方法:
txtLogin.LostFocus += delegate
txtLogin.Text = "Login...";
txtLogin.ForeColor = SystemColors.InactiveCaptionText;
;
好处是您不必指定参数 - 这让您更清楚地知道您没有使用它们。这是匿名方法优于 lambda 表达式的唯一优势。
对性能的影响几乎总是可以忽略不计。如果您确实需要能够删除处理程序,那么之后无法删除它们是一个非常现实的问题,但我发现我经常不需要。 (Reactive Extensions 有一个很好的方法——当你订阅一个可观察的序列时,你会得到一个 IDisposable
,如果你调用它,它将删除订阅。非常整洁。)
【讨论】:
什么是“您确实需要删除处理程序”的好例子?而且,如果多次调用使用 lambda 或匿名方法的方法会怎样? @DaemonPainter:任何时候你想添加一个处理程序,然后再删除它,例如如果您想在操作期间订阅按钮单击事件,请取消订阅。 (这种情况在您身临其境时更容易识别,而不是在不必描述整个应用程序的情况下提出具体示例。)我不确定您的第二条评论是什么意思 - 我怀疑答案将取决于确切您的意思是什么代码。如果您有兴趣,可能值得提出一个新问题。【参考方案2】:实际上,它考虑将事件处理程序放在容易找到的位置,即紧挨着分配给它的事件名称。
很多时候,您会看到如下事件处理程序:
void Text1_KeyDown(....) ....
附加到 txtFirstName 的 KeyUp 事件,因为在使用 Intellisense 创建处理程序后,有人决定重命名文本框,并且 KeyUp 效果更好。使用 Lambda,对象、事件和函数都在一起。
【讨论】:
【参考方案3】:这是一个棘手的问题。我记得在Code Complete 中读到过一些(聪明的)人说你应该让控制流尽可能简单,许多人争论一个方法的单一入口和出口点,因为不这样做会使程序更难遵循.
Lambda 离这一点越来越远,在某些情况下很难跟踪正在发生的事情,控制从一个地方跳到另一个地方。
基本上,我认为这可能是个坏主意,但它也很强大,让生活更轻松。我当然会大量使用它们。总之,慎用!
【讨论】:
将自己限制在一个出口点是可读性灾难的秘诀,IMO。如果我在一行之后知道某个方法的结果(例如,因为它是一种特殊情况),那么就没有理由让读者遵循该方法的其余部分以到达退出点。 我同意,我总是赞成早日退出。但我知道有些人反对这一点。 我的理念是,通常希望避免在具有副作用的第一个语句和最后一个这样的语句之间退出函数,除非从try
块中返回值可能是比在块中设置变量并从外部返回它更干净。以上是关于将 lambda 表达式用于事件处理程序的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章