65.9K
CodeProject 正在变化。 阅读更多。
Home

匿名方法作为事件处理程序 – 第 1 部分

starIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

1.00/5 (1投票)

2009 年 9 月 1 日

CPOL

2分钟阅读

viewsIcon

16573

匿名方法作为事件处理程序

匿名方法提供的语法糖使其成为编写事件处理程序的绝佳选择;结合智能类型推断,它们可以将编写的代码量减少一个数量级。

这还不包括闭包提供的强大功能。对于事件处理程序,闭包允许你将额外的参数“填充”到处理程序中,而无需更改实际的形参数量。 这篇博文展示了一个匿名方法作为事件处理程序可以简化代码的情况,然后展示了在使用取消订阅和匿名方法时的一个陷阱。

这是一个触发许多事件的简单 Control 类。

   1: public void Initialize()
   2: {
   3:     control.KeyPressed += IfEnabledThenDo(control_KeyPressed);
   4:     control.MouseMoved += IfEnabledThenDo(control_MouseMoved);
   5: }
   6:  
   7: public void Destroy()
   8: {
   9:     control.KeyPressed -= IfEnabledThenDo(control_KeyPressed);
  10:     control.MouseMoved -= IfEnabledThenDo(control_MouseMoved);
  11: }

假设你正在使用这个类开发一个 GUI 应用程序,并且只想在源控件在视觉上启用(即,Enabled 设置为 true)时处理事件。 这是一个相当合理的约束,但如果你采用标准方法,将检查添加到每个事件处理程序的开头,则实现起来会很繁琐。

   1: class GUIApp
   2: {
   3:     public void Initialize()
   4:     {
   5:         Control control = new Control();
   6:         control.KeyPressed += new EventHandler<control.controleventargs>(control_KeyPressed);
   7:         control.MouseMoved += new EventHandler<control.controleventargs>(control_MouseMoved);
   8:     }
   9:  
  10:     void control_MouseMoved(object sender, Control.ControlEventArgs e)
  11:     {
  12:         if (e.Control.Enabled)
  13:         {
  14:             /// 
  15:         }
  16:     }
  17:  
  18:     void control_KeyPressed(object sender, Control.ControlEventArgsEventArgs e)
  19:     {
  20:         if (e.Control.Enabled)
  21:         {
  22:             ///
  23:         }
  24:     }
  25: }

使用匿名方法,你可以编写一个更简洁且更易于维护的版本。

   1: class GUIApp
   2: {
   3:     public void Initialize()
   4:     {
   5:         Control control = new Control();
   6:         control.KeyPressed += IfEnabledThenDo(control_KeyPressed);
   7:         control.MouseMoved += IfEnabledThenDo(control_MouseMoved);
   8:     }
   9:  
  10:     public EventHandler<control.controleventargs> 
              IfEnabledThenDo(EventHandler<control.controleventargs> actualAction)
  11:     {
  12:         return (sender, args) => { if (args.Control.Enabled) { actualAction(sender, args); } };
  13:     }
  14:  
  15:     void control_MouseMoved(object sender, Control.ControlEventArgs e)
  16:     {
  17:         ///
  18:     }
  19:  
  20:     void control_KeyPressed(object sender, Control.ControlEventArgs e)
  21:     {
  22:         ///
  23:     }
  24: }

IfEnabledThenDo 返回一个匿名函数,它首先检查控件是否启用,然后再调用实际函数。 代码更短,并且只在一个地方检查条件,这使得修改或添加其他逻辑变得容易,而无需记住更改每个事件处理程序。 此外,它看起来像一个英文语句——订阅事件,如果启用,则执行其他必要的操作。

很好,但是,除非你是一个喜欢在代码库中散布难以重现的错误,并且只在向最重要的客户演示时才会炸毁应用程序的自虐狂,否则你当然必须编写代码来取消订阅。 但是,没有方法名称可以引用,因此你以与订阅时相同的方式进行操作。

   1: public void Initialize()
   2: {
   3:     control.KeyPressed += IfEnabledThenDo(control_KeyPressed);
   4:     control.MouseMoved += IfEnabledThenDo(control_MouseMoved);
   5: }
   6:  
   7: public void Destroy()
   8: {
   9:     control.KeyPressed -= IfEnabledThenDo(control_KeyPressed);
  10:     control.MouseMoved -= IfEnabledThenDo(control_MouseMoved);
  11: }

不幸的是,这不起作用——应用程序仍然会订阅这些事件。 你能弄清楚为什么吗?

答案以及更多内容将在下一篇博文中。

© . All rights reserved.