C# 事件处理程序的Lambdas?

C# 事件处理程序的Lambdas?,c#,lambda,event-handling,C#,Lambda,Event Handling,C#3中的Lambda语法使得创建一行匿名方法非常方便。与C#2给我们的更冗长的匿名委托语法相比,它们是一个明显的改进。然而,lambdas的便利性带来了在我们不一定需要它们提供的函数式编程语义的地方使用它们的诱惑 例如,我经常发现我的事件处理程序是(或至少一开始是)简单的一行程序,用于设置状态值、调用另一个函数或设置另一个对象的属性等。对于这些,我应该用另一个简单函数来混乱我的类,还是应该在构造函数中将lambda填充到事件中 在这种情况下,lambdas有一些明显的缺点: 我不能直接调用我

C#3中的Lambda语法使得创建一行匿名方法非常方便。与C#2给我们的更冗长的匿名委托语法相比,它们是一个明显的改进。然而,lambdas的便利性带来了在我们不一定需要它们提供的函数式编程语义的地方使用它们的诱惑

例如,我经常发现我的事件处理程序是(或至少一开始是)简单的一行程序,用于设置状态值、调用另一个函数或设置另一个对象的属性等。对于这些,我应该用另一个简单函数来混乱我的类,还是应该在构造函数中将lambda填充到事件中

在这种情况下,lambdas有一些明显的缺点:

  • 我不能直接调用我的事件处理程序;它只能由事件触发。当然,对于这些简单的事件处理程序,我几乎不需要直接调用它们
  • 我无法将我的处理程序与事件解除挂钩。另一方面,我很少需要解开事件处理程序的挂钩,所以这也不是什么大问题
出于上述原因,这两件事不太让我烦恼。我可以通过将lambda存储在成员委托中来解决这两个问题,如果它们真的是问题的话,但这可能会有点违背使用lambda的目的,因为它们很方便,并且保持类没有混乱

不过,我认为还有两件事可能不那么明显,但可能问题更大

  • 每个lambda函数在其包含范围上形成一个闭包。这可能意味着,由于闭包维护对临时对象的引用,在构造函数早期创建的临时对象保持活动的时间比它们需要的时间长得多。现在希望编译器足够聪明,可以从lambda不使用的闭包中排除对象,但我不确定。有人知道吗

    幸运的是,这并不总是一个问题,因为我不经常在构造函数中创建临时对象。我可以想象这样一个场景,虽然我做到了,但我不能轻易地在lambda之外对其进行观察

  • 可维护性可能会受到影响。重要时刻。如果我将一些事件处理程序定义为函数,而将一些事件处理程序定义为lambdas,我担心这可能会使跟踪bug或仅仅理解类变得更加困难。稍后,如果我的事件处理程序最终扩展,我将不得不将它们移动到类级函数,或者处理这样一个事实,即我的构造函数现在包含大量实现类功能的代码
因此,我想借鉴其他人的建议和经验,也许是那些具有其他语言功能编程经验的人。对于这类事情有什么既定的最佳实践吗?您是否会避免在事件处理程序中使用lambda,或者在lambda明显超出其封闭范围的其他情况下使用lambda?如果不是,您会在什么阈值下决定使用实函数而不是lambda?上述任何一个陷阱是否对任何人都有重大影响?有什么我没想到的陷阱吗

每个lambda函数在其包含范围上形成一个闭包。这可能意味着,由于闭包维护对临时对象的引用,在构造函数早期创建的临时对象保持活动的时间比它们需要的时间长得多。现在希望编译器足够聪明,可以从lambda不使用的闭包中排除对象,但我不确定。有人知道吗

根据我所读的内容,C#编译器要么生成匿名方法,要么生成匿名内部类,这取决于它是否需要关闭包含范围

换句话说,如果不从lambda中访问包含范围,它将不会生成闭包

不过,这有点“道听途说”,我希望有一位对C#编译器更了解的人也能参与进来

尽管如此,旧的C#2.0匿名委托语法也做了同样的事情,我几乎总是将匿名委托用于短事件处理程序


您已经很好地介绍了各种优缺点,如果您需要解开事件处理程序的挂钩,请不要使用匿名方法,否则我完全赞成。

我通常有一个例程专门用于连接事件处理程序。其中,我使用匿名委托或lambda作为实际的处理程序,使它们尽可能短。这些处理程序有两个任务:

  • 解压缩事件参数
  • 使用适当的参数调用命名方法

  • 这样,我就避免了将我的类名称空间与无法干净地用于其他目的的事件处理程序方法混为一谈,并迫使自己考虑我实现的操作方法的需要和目的,通常会产生更干净的代码

    lambdas的大多数相同特性同样适用于您可以使用它们的其他地方。如果事件处理程序不适合他们,我想不出更好的了。它是一个单点独立的逻辑单元,位于它的单点上

    在许多情况下,事件的目的是获得一个小的上下文包,结果证明它正好适合手头的工作


    我认为这是重构意义上的“好气味”之一。

    < P>根据编译器的一个小实验,我会说编译器足够聪明,可以创建一个闭包。我所做的是一个简单的构造函数,它有两个不同的lambda,用于List.Find()中的谓词

    第一个lamdba使用硬编码值,第二个使用构造函数中的参数。第一个lambda作为类上的私有静态方法实现。第二个lambda被实现为执行关闭的类

    因此,您认为编译器足够聪明的假设是正确的。

    Conc
    public class UserIsUnhappy : Notification { public int unhappiness; }