Effective C# 原则35:选择重写函数而不是使用事件句柄(译)

Effective C# 原则35:选择重写函数而不是使用事件句柄
Item 35: Prefer Overrides to Event Handlers

很多.Net类提供了两种不同的方法来控制一些系统的事件。那就是,要么添加一个事件句柄;要么重写基类的虚函数。为什么要提供两个方法来完成同样的事情呢?其实很简单,那就是因为不同的情况下要调用为的方法。在派生类的内部,你应该总是重写虚函数。而对于你的用户,则应该限制他们只使用句柄来响应一些不相关的对象上的事件。

例如你很了一个很不错的Windows应用程序,它要响应鼠标点下的事件。在你的窗体类中,你可以选择重写OnMouseDown()方法:

public class MyForm : Form
{

  // Other code elided.

  protected override void OnMouseDown(
    MouseEventArgs e )
  {
    try {
      HandleMouseDown( e );
    } catch ( Exception e1 )
    {
      // add specific error handling here.
    }
    // *almost always* call base class to let
    // other event handlers process message.
    // Users of your class expect it.
    base.OnMouseDown( e );
  }
}

或者你可以添加一个事件句柄:


public class MyForm : Form
{

  // Other code elided.

  public MyForm( )
  {
    this.MouseDown += new
      MouseEventHandler( this.MouseDownHandler );
  }

  private void MouseDownHandler( object sender,
    MouseEventArgs e )
  {
    try {
      HandleMouseDown( e );
    } catch ( Exception e1 )
    {
      // add specific error handling here.
    }
  }
}

前面一些方法要好一些,如果在事件链上有一个句柄抛出了一个异常,那么其它的句柄都不会再被调用(参见原则21)。一些“病态”的代码会阻止系统调用事件上的句柄。通过重写受保护的虚函数,你的控制句柄会就先执行。基类上的虚函数有责任调用详细事件上的所有添加的句柄。这就是说,如果你希望事件上的句柄被调用(而且这是你最想完成的),你就必须调用基类。而在一些罕见的类中,你希望取代基类中的默认事件行为,这样可以让事件上的句柄都不被执行。你不去保证所所的事件句柄都将被调用,那是因为一些“病态”事件句柄可能会引发一些异常,但你可以保证你派生类的行为是正确的。

使用重载比添加事件句柄更高效。我已经在原则22中告诉过你,System.Windows.Forms.Control类是如何世故的使用任命机制来存储事件句柄,然后映射恰当的句柄到详细的事件上。这种事件机制要花上更多的处理器时间,那是因为它必须检测事件,看它是否有事件句柄添加在上面。如果有,它就必须迭代整个调用链表。方法链表中的每个方法都必须调用。断定有哪些事件句柄在那里,还要对它们进行运行时迭代,这与只调用一个虚函数来说,要花上更多的执行时间。

如果这还不足以让你决定使用重载,那就再看看这一原则一开始的链表。那一个更清楚?如果重载虚函数,当你在维护这个窗体时,只有一个函数要检查和修改。而事件机制则有两个地方要维护:一个就是事件句柄,另一就是事件句柄上的函数。任何一个都可能出现失败。就一个函数更简单一些。

OK,我已经给出了所有要求使用重载而不是事件句柄的原因。.Net框架的设计者必须要添加事件给某人,对吗?当然是这样的。就你我们剩下的内容一个,他们太忙了而没时间写一些没人使用的代码。重写只是为派生类提供的,其它类必须使用事件机制。例如,你经常添加一个按钮点击事件到一个窗体上。事件是由按钮触发的,但是由窗体对象处理着事件。你完全可以在这个类中定义一个用户的按钮,而且重写这个点击句柄,但这对于只是处理一个事件来说花上了太多的代码。不管怎样,问题都是交给你自己的类了:你自己定义的按钮还是在点击时必须与窗体进行通信。显然应该用事件来处理。因此,最后,你只不过是创建了一个新类来向窗体发送事件(译注:其实我们完全可以创建这个类不用发事件给窗体就可以完成回调的,只是作者习惯的说什么好就一味的否定其它。但不管怎样,重写一个按钮来重载函数确实不是很值。)。 相对前面一种方法,直接在窗体事件添加句柄要简单得多。这也就是为什么.Net框架的设计者把事件放在窗体的最前面。

另一个要使用事件的原因就是,事件是在运行时处理的。使用事件有更大的伸缩性。你可以在一个事件上添加多个句柄,这取决于程序的实际环境。假设你写了一个绘图程序,根据程序的状态,鼠标点下时应该画一条线,或者这它是要选择一个对象。当用户切换功能模式时,你可以切换事件句柄。不同的类,有着不同的事件句柄,而处理的事件则取决于应用程序的状态。

最后,对于事件,你可以把多个事件句柄挂到同样的事件上。还是想象同样的绘图程序,你可能在MouseDown事件上挂接了多个事件句柄。第一个可能是完成详细的功能,第二个可能是更新状态条或者更新一些可访问的不同命令。不同的行为可以在同一事件上响应。

当你有一个派生类中只有一个函数处理一个事件时,重载是最好的方法。这更容易维护,今后也会更正确,而且更高效。而应该为其它用户保留事件。因此,我们应该选择重写基类的实现而不是添加事件句柄。
=======================
   

Item 35: Prefer Overrides to Event Handlers
Many .NET classes provide two different ways to handle events from the system. You can attach an event handler, or you can override a virtual function in the base class. Why provide two ways of doing the same thing? Because different situations call for different methods, that's why. Inside derived classes, you should always override the virtual function. Limit your use of the event handlers to responding to events in unrelated objects.

You write a nifty Windows application that needs to respond to mouse down events. In your form class, you can choose to override the OnMouseDown() method:

public class MyForm : Form
{

  // Other code elided.

  protected override void OnMouseDown(
    MouseEventArgs e )
  {
    try {
      HandleMouseDown( e );
    } catch ( Exception e1 )
    {
      // add specific error handling here.
    }
    // *almost always* call base class to let
    // other event handlers process message.
    // Users of your class expect it.
    base.OnMouseDown( e );
  }
}

 

Or, you could attach an event handler:

public class MyForm : Form
{

  // Other code elided.

  public MyForm( )
  {
    this.MouseDown += new
      MouseEventHandler( this.MouseDownHandler );
  }

  private void MouseDownHandler( object sender,
    MouseEventArgs e )
  {
    try {
      HandleMouseDown( e );
    } catch ( Exception e1 )
    {
      // add specific error handling here.
    }
  }
}

 

The first method is preferred. If an event handler throws an exception, no other handlers in the chain for that event are called (see Item 21). Some other ill-formed code prevents the system from calling your event handler. By overriding the protected virtual function, your handler gets called first. The base class version of the virtual function is responsible for calling any event handlers attached to the particular event. That means that if you want the event handlers called (and you almost always do), you must call the base class. In some rare cases, you will want to replace the default behavior instead of calling the base class version so that none of the event handlers gets called. You can't guarantee that all the event handlers will be called because some ill-formed event handler might throw an exception, but you can guarantee that your derived class's behavior is correct.

Using the override is more efficient than attaching the event handler. I showed you in Item 22 how the System.Windows.Forms.Control class uses a sophisticated collection mechanism to store event handlers and map the appropriate handler to a particular event. The event-handling mechanism takes more work for the processor because it must examine the event to see if any event handlers have been attached. If so, it must iterate the entire invocation list. Each method in the event invocation list must be called. Determining whether there are event handlers and iterating each at runtime takes more execution time than invoking one virtual function.

If that's not enough for you, examine the first listing in this item again. Which is clearer? Overriding a virtual function has one function to examine and modify if you need to maintain the form. The event mechanism has two points to maintain: the event handler function and the code that wires up the event. Either of these could be the point of failure. One function is simpler.

Okay, I've been giving all these reasons to use the overrides and not use the event handlers. The .NET Framework designers must have added events for a reason, right? Of course they did. Like the rest of us, they're too busy to write code nobody uses. The overrides are for derived classes. Every other class must use the event mechanism. For example, you often add a button click handler in a form. The event is generated by the button, but the form object handles the event. You could define a custom button and override the click handler in that class, but that's way too much work to handle one event. It only moves the problem to your own class anyway: Somehow, your custom button must communicate to the form that the button was clicked. The obvious way to handle that is to create an event. So, in the end, you have created a new class to send an event to the form class. It would be simpler to just attach the form's event handler to the form in the first place. That's why the .NET Framework designers put those events in the forms in the first place.

Another reason for the event mechanism is that events are wired up at runtime. You have more flexibility using events. You can wire up different event handlers, depending on the circumstances of the program. Suppose that you write a drawing program. Depending on the state of the program, a mouse down might start drawing a line, or it might select an object. When the user switches modes, you can switch event handlers. Different classes, with different event handlers, handle the event depending on the state of the application.

Finally, with events, you can hook up multiple event handlers to the same event. Imagine the same drawing program again. You might have multiple event handlers hooked up on the MouseDown event. The first would perform the particular action. The second might update the status bar or update the accessibility of different commands. Multiple actions can take place in response to the same event.

When you have one function that handles one event in a derived class, the override is the better approach. It is easier to maintain, more likely to be correct over time, and more efficient. Reserve the event handlers for other uses. Prefer overriding the base class implementation to attaching an event handler.
 
   

   

转载于:https://www.cnblogs.com/WuCountry/archive/2007/03/26/688983.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300767.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WPF实现统计图(饼图仿LiveCharts)

WPF开发者QQ群: 340500857 | 微信群 -> 进入公众号主页 加入组织欢迎转发、分享、点赞、在看,谢谢~。 01—效果预览效果预览(更多效果请下载源码体验):一、PieControl.cs 代码如下 using System.Collections.Obje…

酷桌面:随身携带你的企业

需求背景:1.当前,很多企业把建设一个宣传型网站作为互联网宣传的第一步,在互联网上展示企业形象和主营业务,吸引浏览者关注其网站,从而达到促进销售、提升企业价值的作用。然而在移动端,不仅是将原有的PC网…

dataframe 修改某列_python dataframe操作大全数据预处理过程(dataframe、md5)

hive表的特征选择,不同表之间的join训练数据、测试数据的分开保存使用pandas进行数据处理显示所有列:pd.set_option(display.max_columns, None)显示所有行:pd.set_option(display.max_rows, None)单列运算:df[col2] = df[col1].map(lambda x: x**2)多列运算:df[col3] = d…

计算机实践教程采莲趣事,计算机基础作业采莲趣事

精品文档 . 忽然想起采莲的事情来了。采莲是江南的旧俗,似乎很早就有,而六朝时为盛;从诗歌里可以约略知道。采莲的是少年的女子,她们是荡着小船,唱着艳歌去的。采莲人不用说很多,还有看采莲的人。那是一个热…

那些曾经拥有的最大快乐,都是好奇心的结果

▲ 点击查看对于孩子们来说,强烈的好奇心和求知欲,是一种本能。在他们懵懵懂懂长大的过程中,总是对周围的世界充满着各种各样的疑问:“叶子为什么是绿色的?”“为什么花朵有那么多种颜色?”“蚂蚁为什么能…

[原]让链接点击过后无虚线

我以前还以为有难呢,在网上查资料,才知道这么简单, <a href"http://www.ktbbs.com"onfocus"this.blur()">转载于:https://www.cnblogs.com/Kennytian/archive/2007/03/31/695463.html

.net core 下使用StackExchange的Redis库访问超时解决

问题&#xff1a;并发稍微多的情况下Redis偶尔返回超时StackExchange的Redis类库&#xff0c;用的较多&#xff0c;但偶尔报的问题非常让人迷惑&#xff0c;访问超时&#xff0c;队列XXX…问题出在Redis服务器吗&#xff1f;可是其他应用访问都正常啊&#xff0c;难道要把这个类…

UML简易实践

2019独角兽企业重金招聘Python工程师标准>>> 面向对象的问题的处理的关键是建模问题。建模可以把在复杂世界的许多重要的细节给抽象出。许多建模工具封装了UML&#xff08;也就是Unified Modeling Language™&#xff09;&#xff0c;这篇课程的目的是展示出UML的精…

囊括计算机 电子信息仿真技术,什么是虚拟现实?

虚拟现实技术(英文名称&#xff1a;Virtual Reality&#xff0c;缩写为VR)&#xff0c;又称灵境技术&#xff0c;是20世纪发展起来的一项全新的实用技术。虚拟现实技术囊括计算机、电子信息、仿真技术于一体&#xff0c;其基本实现方式是计算机模拟虚拟环境从而给人以环境沉浸感…

io python 读取pdf_Python读取PDF文件--pdfminer

作者使用的是Python3.6版本。pdfminer在Python2和Python3中的安装和使用有一定的区别&#xff0c;本文以Python为例。首先安装pdfminerpip install pdfminer3k官网对PDFMiner的介绍如下&#xff1a;PDFMiner is a tool for extracting information from PDF documents. Unlike …

战斗机各种世界之最,涨知识了。。。

全世界只有3.14 % 的人关注了青少年数学之旅世界上最大的战斗机苏联的图-128是目前世界上起飞重量最大&#xff0c;体积最大的一款截击机&#xff0c;它由前苏联图波列夫设计局于1955研制成功并进行首飞&#xff0c;1963年装备部队&#xff0c;该机全长30.03米&#xff0c;翼展…

Bye Bye Embed-再见了Embed,符合web标准的媒体播放器代码

由于Embed标签是Netscape的私有财产&#xff0c;故一直未被W3C认可&#xff0c;对于各种媒体文件使用Embed标签是非标准的&#xff0c;如何改变&#xff1f;Elizabeth Castro的 Bye Bye Embed 一文对于各种媒体播放器给出了很好的符合web标准的代码。 在线媒体播放--Google Vid…

.NET Core授权失败如何自定义响应信息?

【导读】在.NET 5之前&#xff0c;当授权失败即403时无法很友好的自定义错误信息&#xff0c;以致于比如利用Vue获取到的是空响应&#xff0c;不能很好的处理实际业务&#xff0c;同时涉及到权限粒度控制到控制器、Action&#xff0c;也不能很好的获取对应路由信息本文我们来看…

计算机电缆2x2x1.5,计算机电缆djypvp1x2x1.5

计算机电缆djypvp1x2x1.5硅橡胶计算机电缆适用于各种仪器仪表的连结&#xff0c; 以及信号传输。它具有耐高温、低温、耐腐蚀、舒缓老化等优点&#xff0c;对特殊场所或恶劣环境中的使用是目前理想的产品&#xff0c;正常使用温度为—60℃一250℃。产品特点:1.执行标准Q/ILXD-1…

jQuery实现等比例缩放大图片让大图片自适应页面布局

在布局页面时&#xff0c;有时会遇到大图片将页面容器“撑破”的情况&#xff0c;尤其是加载外链图片&#xff08;通常是通过采集的外站的图片&#xff09;。那么本文将为您讲述使用jQuery如何按比例缩放大图片&#xff0c;让大图片自适应页面布局。通常我们处理缩略图是使用后…

虚线 实现_redis跳跃表实现

跳跃表是一种有序的数据结构&#xff0c;它通过在每个节点中维持多个指向其他节点的指针&#xff0c;从而达到快速访问节点的目的。redis 使用跳跃表作为有序集合键的底层实现之一&#xff0c;如果一个有序集合包含的元素数量比较多&#xff0c;又或者有序集合中元素的成员是比…

当代家长现状。。 | 今日最佳

世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源都市音酱&#xff09;太真实了↓ ↓ ↓

使用目录服务和 Visual C# .NET 向本地系统添加用户

创建示例1. 打开 Microsoft Visual Studio .NET&#xff0c;然后新建一个 Visual C# Console 应用程序项目。 2. 在“解决方案资源管理器”中&#xff0c;右键单击引用&#xff0c;然后单击添加引用。 3. 添加一个对 System.DirectoryServices.dll 程序集的引用。 4. 将 Clas…

Prometheus(一):Web服务环境监控

&#xfeff;写在前面现每个后端的同学的日常都在跟服务(接口)打交道&#xff0c;维护老的比较大单体应用、按业务拆得相对比较细的新服务、无论企业内部用的&#xff0c;面向用户的前端的服务。流量大的有流量小的&#xff0c;有重要的有不那么重要的。但是&#xff0c;不管怎…

自拟计算机作文100字,我的电脑作文100字五篇

我家有一台电脑&#xff0c;颜*是黑*的&#xff0c;电脑的作用是可以玩游戏&#xff0c;还可以查自己不懂的问题。有一次&#xff0c;爸爸和妈妈不在家&#xff0c;我就先做完作业&#xff0c;在做作业的时有一道题我不知道怎么做&#xff0c;于是&#xff0c;我上百度上一搜&a…