如何编写没有Try/Catch的程序
在上面一篇文章《谈谈关于MVP模式中V-P交互问题》中,我提到最近一直为一个项目进行Code Review的工作,从中发现了一些问题,同时也有了一些想法。上次谈到如何正确编写服务MVP规范的程序,这次我们来关注一个我们每天都会面对的问题:异常处理。 一、异常处理不简单个人觉得,异常处理对于程序员来说,尤其是对于那些初级.NET程序员来说,是最为熟悉的同时也是最难掌握的。说它熟悉,因为仅仅就是Try/Catch而已。说它难以掌握,很多开发人员却说不清楚Try/Catch应该置于何处?什么情况下需要对异常进行日志记录?什么情况下需要对异常进行封装?什么情况下需要对异常进行替换?对于捕获的异常,在什么情况下需要将其再次抛出?什么情况下则不需要。总之,异常处理没有我们想象的那么简单。 无论对于何种类型的应用,异常处理都是必不可少的。合理的异常处理应该是场景驱动的,在不同的场景下,采用的异常处理策略往往是不同的。异常处理的策略应该是可配置的,因为应用程序出现怎样的异常往往是不可预测的,现有异常策略的不足往往需要在真正出现某种异常的时候才会体现出来,所以我们需要一种动态可配置的异常处理策略维护方式。目前有一些开源的异常处理框架提供了这种可配置的、场景驱动的异常处理方式,EnterLib的Exception Handling Application Block就是一个不错的选择。 二、异常处理对于最终的开发人员是透明的“异常处理对于最终的开发人员是透明的”,可能这句话说得有点过头。但是,就我个人的项目经验来讲,这是一种理想的状态。由于异常策略是一般是通过配置动态配置的,不需要反映在代码上面。如果能够通过框架的方式提供异常处理的实现,使开发人员无需编写任何异常处理的代码,只需要关注业务流程的实现就可以了,这不仅能够提高开发的效率,也能够提高系统的可维护性。 我们目前的项目是一个典型的分布式应用,所有的业务流程的处理和数据访问都实现在服务端,最终以WCF服务的形式暴露给客户端(Smart Client)和第三方应用。所有客户端和服务端从逻辑上具有相应的层次划分,但是异常处理仅仅实现在两个地方,一个地方是WCF服务本身,另一个实现UI层。忘了说明一点,我们项目直接将EnterLib的Exception Handling Application Block作为我们的异常处理框架。对于服务端的异常处理来说,我们通过WCF与EHAB的集成来实现的(《WCF与Exception Handling AppBlock集成》),所以不需要开发人员添加任何一句Try/Catch代码。但是客户端来说,对于某个控件的事件来说,由于UI本身就是处于整个调用栈的最顶层,很难通过基于AOP的拦截机制来实现对异常处理的动态注入,所以客户端会出现非常类似于下面代码所示的Try/Catch。 1: private void buttonCalculate_Click(object sender,EventArgs e) 3: try
5: //
7: catch (Exception ex)
9: if (ExceptionPolicy.HandleException(ex,"policyName")) 11: throw;
13: } 15: } 我是一个对重复代码具有强迫症的人,看到两个相同的代码我都有对代码进行重构的冲动,何况如此众多的相同代码充斥在客户端。
三、通过编写公共方法的形式实现代码的重用为了避免开发人员编写相同的Try/Catch,很多人首先想到的肯定是将重复代码定义在一个公共的方法上,以实现代码的复用。这个公共方法很简单,只需要如下几句代码即可。
5: action();
14: } 在调用的时候,只需要将相应的操作以Action类型的Delegate的形式传入Invoke方法即可。但是这样,也会在所有控件处理事件中出现重复的Invoke调用,虽然重复的代码行数减少了,但是还是会出现大规模的重复。接下里我来介绍另一种解决方法。 四、对EventHandler进行封装认真分析上面的需求,我们的根本目的就是让执行事件处理程序的时候在外面人为地套一个Try/Catch,并对捕获的异常进行相应的处理。从这个意义上讲,如果我们能够对EventHandler或者ExventHandler<TEventArgs>进行相应的封装,就能实现我们需要的目的。 可能我这样说,你不会太明白,我们还是通过代码来说话好了。在下面我创建了一个用于封装EventHandler对象的EventHandlerWrapper类型。我们知道EventHandler是一个Delegate,而Delegate由两部分组成:表示操作本身的MethodInfo和操作执行的目标对象,分别通过属性Method和Target表示。在执行EventHandler的时候,就是通过反射的方式调用MethodInfo的Invoke方法,并将目标对象和相应的参数传入该方法而已。 2: using System.Diagnostics;
4: using System.Text;
6: namespace ProgramingWithoutTryCatch
8: class EventHandlerWrapper
10: object Target
12:? 14: { get; private set; }
16: public EventHandler Hander
19: public EventHandlerWrapper(EventHandler eventHandler)
21: if (null == eventHandler) 23: throw new ArgumentNullException("eventHandler"); 25:? 27: this.Method = eventHandler.Method;
29: } 31: static implicit operator EventHandler (EventHandlerWrapper eventHandlerWrapper) 33: return eventHandlerWrapper.Hander;
35:? 37: { 39: { 41: } 43: { 45: message.AppendLine(string.Format("Message: {0}",ex.InnerException.Message)); 47: message.AppendLine("Stack Trace: {0}",ex.InnerException.StackTrace));
49: MessageBox.Show(ex.InnerException.Message + Environment.NewLine + "For detailed information,please view event log",1)">string.Empty,MessageBoxButtons.OK,MessageBoxIcon.Error);
51: } 53: } namespace ProgramingWithoutTryCatch
5: partial class Form1 : Form 7: public Form1()
9: InitializeComponent(); 11: } 15: int op1 = int.Parse(this.textBoxOp1.Text); 17: int result = op1 / op2;
19: } 21: } 代码非常简单,需要注意的是在对Button的Click事件进行注册的时候,我们直接使用的时我们上面创建的EventHandlerWrapper,这和真正进行事件注册的方式几乎一致。当你输入非数字或者被除数设置为的时候,会抛出异常,异常的相关信息会直接写入EventLog,并将异常消息通过MessageBox显示出来,如下图所示: 五、通过EventHandlerWrapper的写法实现其他的功能EventHandlerWrapper实际上为了展示了对EventHandler进行封装的方式,异常处理并非其独有的应用场景。如果你看过我的文章《事件 (Event),绝大多数内存泄漏(Memory Leak)的元凶(上篇)(下篇)》,你会发现我通过相同的方式解决了事件注册导致的内存泄露的问题。在这里我在介绍另外一种有趣的应用。 在进行Windows Forms开发中,相信你会经常要求实现这样的功能:如果点击某个按钮后,需要较长的反映时间,需要在点击之后将Form的光标设置成沙漏的形状(Wait Cursor),当整个处理结束后再将其回复。我们可以对EventHandlerWrapper的Invoke方法略加修改就能够实现这个功能: if(null != Form.ActiveForm)
作者:Artech 出处:http://artech.cnblogs.com 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net – 如何在IIS上配置Web部署发布功能,以便开发人员可
- asp.net – 发布网站项目时Temp路径太长
- ASP.NET Core MVC 502 bad gateway 超时如何处理
- asp.net-mvc – 将模型信息传递给RenderPartial
- azure – 从我的ASP.NET网站执行按需Web作业?
- 框架类似于ASP.Net AjaxPro
- 从ASP.NET WebForms迁移到ASP.NET MVC
- asp.net – 如何在占位符中的动态生成的标签之间创建换行符
- asp.net – 即使我在Windows 8中安装了协议,SignalR也不会使
- asp.net – 根据DropDownList选择验证TextBox
- asp.net-mvc – 使用IIS基本身份验证的OWIN身份验
- asp.net-mvc – ASP.NET MVC,将Model从View传递给
- asp.net – 在Visual Studio中编辑文件后,如何解
- asp.net – 哪个更适合性能视图状态或会话
- asp.net – 我的URL中的这些随机字符是什么?它们
- asp.net – 使下拉列表项不可选
- .net – RegularExpressionValidator使用除R??eg
- asp.net-mvc-3 – ASP.NET MVC 3:如何在控制器方
- 在为ASP.net构建期间缩小内联JavaScript?
- asp.net-mvc – 为生成的帮助标签添加冒号和星号