一起谈.NET技术,谈谈我处理异常的一般方法

  我们在编写程序的时候会遇到各种各样的意外情况,如除数为0,数组越界,非法转型,栈溢出等等。因而我们需要有一种机制来处理这些情况,异常处理就是其中的一种机制。当然,还有其他的机制,在MFC中,由于标准的不统一,就存在着各种错误报告方法,如有通过函数返回特殊值的方式,有通过执行某一语句后查询特殊语句获取错误的码的方式,等等。

  在C#中,只有一种报告方式,即异常。这样可以让开发人员从大量的文档中解脱出来,不必为一些非逻辑的问题而花费大量时间。

  C#中的异常不同于C++,所有的异常类型都是继承自System.Exception的,因此我们定义自己的异常类型时,都要继承自System.Exception或者该类的子类。

  常见的异常语句如下:

try{
  …
}catch( InvalidOperationException ex){
  ..
}catch( Exception ex){
  ..
  throw ex;
}
}finally{
  ..
}

  我们可以通过捕获不同类型的异常来捕获我们需要的信息,而最后一个捕获Exception的语句将会捕获未被该语句之前的语句捕获的信息。Throw语句可以将捕获的异常抛出,继续引发该异常,使得外层的捕获语句捕获到该异常,当然,最原始的异常也是通过该语句引发的。

  以上都是一些基本的异常知识。我希望根据自己的经验,阐述一下自己开发过程中是如何使用异常的。

 

  首先要解决的问题是何时使用异常。我们知道,引发一个异常,需要一个比较大的开销(http://www.cnblogs.com/aoaoblogs/archive/2009/12/08/1619827.html)。我认为,对于在处理问题主要逻辑范围以内的问题,不要使用异常。主要逻辑范围,指的是在完成一件任务的主要路径。

  比如我现在要进行一个查询工作,查询一下用户在不在,如果该用户在,那么我应该返回true,不在则应该返回false,我在用这个方法的时候,我对他的直接期望是得到一个返回值。然而,在查询过程中会发生一些其他错误,如数据库无法连接,数据库并发连接数过大。这些问题,就应当是异常处理所要面对的问题。又比如我希望上传一个文件,我用的时候就直接调用该方法,该语句在设计之初,就应当是直接能上传的。

 

  还有就是什么样的问题需要抛出。

  我认为,在设计过程中,在一些不可恢复的情况下,才有抛出异常的必要。应当在文档中列出较少的可能抛出的异常。我们继续以查询用户为例子,在查询数据库的过程中,我们发现数据库无法连接,如果该查询方法对时间要求不是很严格,我们应当使用一些策略来使得该方法能完成,如再次尝试连接。在尝试连接一定次数之后,我们才需要引发异常。

  还有我认为,我们在设计方法的时候,要捕获所有的方法内语句产生的异常,并将异常包装后抛出,而不是直接抛出。并且抛出的异常最好是与逻辑相关的。继续用查询用户做例子,数据库链接出错、网络不可用之类的问题,我认为为了方便开发者使用该类(其实有时候开发者往往就是自己),我们应当将这些异常都放到一个异常类型中,如EnvironmentException,然后定义一个枚举,将该枚举作为EnvironmentException的属性。我们在捕获异常的时候,只要捕获该类型的异常就好了,如果希望了解异常细节,我们则可以使用InnerException获取引起该异常的异常。

 

  下面讨论怎样定义异常的问题。

  在.net中有许多内置的异常,如InvalidOperationException、InvalidCastException等,这些异常都继承自System.SystemException。我在定义异常的时候,通常直接继承自System.Exception。我个人认为,一个方法不应该抛出太复杂的异常,而使得开发者困惑。我抛出一个异常,只是把一个方法执行过程中所存在的问题抛出了。我想,得到一个与这个方法逻辑相关的异常、看到名字就大概知道哪里出错的异常,总比一个InvalidCastException这样的异常好多了。

  比如以下是一段语句。

try{  
  var user=userManager.GetUser(id);
  user.Login();
  user.Logout();
  var article=articleManager.getArticle(articleId);
  article.user=user;
  article.save();
}catch{
  …
}

  如果Login()会抛出一个InvalidOperationException,logout()会抛出一个InvalidOperationException,我们可能不得不进一步分析这些异常,然后才能对这些操作作出适当的应对措施。

  对于一个规模不大的类,我习惯于定义包含一个枚举类型的类的异常。例如,上面这个例子,我会定义UserOperationException, UserManagerOperationException, ArticleOperationExcetpion,ArticleOperation这些异常,以UserOperationException为例子

public class UserOperationException:Exception{
  //仅列出主要部分,构造函数等就略了..
  public enum UserOperationStatus{get;set;}
}
public enum UserOperationStatus{
  Login,
  Logout,
  Save,
}

  这样在异常捕获的时候,我们可以捕获特定的异常来确定哪一块出现了问题,也可以捕获Exception来捕获所有的异常。

  根据我前面说过的设计原则,所有的异常都是经过包装的已知异常,而且这些异常是基于我们的业务逻辑的,这样代码看起来就会比较清晰了。

  如果Login方法还有一些其他的异常信息,我一般这样实现。

public enum LoginOperationException:UserOperationException{
  public LoginOperationException(string message,ExceptioninnerException)
    :base(message,innerException,UserOperationStatus.Login)
  {
    ..
  }
}

  使用这样的继承方式,我们就可以更加灵活的来进行异常处理。

 

  现在讨论一下如何进行异常捕获。

  我认为在写程序的时候,可以首先考虑程序的逻辑问题,非逻辑问题,即异常捕获可以不进行处理。继续以用户登录为例子:

public function void Login(string username,string password){
  //_UserManager是类的一个私有字段
  var user=_UserManager.GetUserByName(username);
  if(user.password==password)
  {
    user.Login();
    Console.WriteLine(“Success”);
    user.Logout();
    Console.WriteLine(“LogoutSuccess”);
  }else{
    Console.WriteLine(“Invalidpassword”);
  }
}

  可能我们第一次考虑的时候是这样的,这是一个最直观的逻辑,在编写代码的过程中,并没有过多的来关注各个方法抛出的异常。当我们检查完毕整个逻辑没有问题之后,就可以对异常进行捕获了。

public function void Login(string username,string password){
  try{
    //_UserManager是类的一个私有字段
    var user=_UserManager.GetUserByName(username);
    if(user.password==password)
    {
      user.Login();
      Console.WriteLine(“Success”);
      user.Logout();
      Console.WriteLine(“Logout Success”);
    }else{
    Console.WriteLine(“Invalid password”);
    }
  }catch(UserManagerOperationException){
    Console.WriteLine(“User doesn’t exist”);
  }catch(UserLoginOperationException ex){
    switch(ex.LoginStatus){
      case LoginStatus.DB:
        Console.WriteLine(“DB errors”);
        break;
      case LoginStatus.Network:
        Console.WriteLine(“Network errors”);
        break;
  }catch(UserOperationExceptionex){
    Console.WriteLine(“Other exception {0}”,ex);
  }
}

  当然了,这样的编码方式还是要在编码过程中来关注这些异常。在我所了解的范围内,微软提供了一个开源的企业库Microsoft Enterprise Library(http://entlib.codeplex.com/),该库提供了一个ExceptionHandling Application Block,我们可以用更加灵活方式来处理这些异常,比如全部在配置文件里面做。

  当然,还有其他的方式来解决异常处理的问题,比如AOP技术。最近一直对AOP比较感兴趣,但并没有相关的实践,因此也没有什么经验可谈。

  如果您完整的看完了我的文章,希望您可以根据您的经验指出文章中存在的问题。这篇文章只是一个我的经验总结。

时间: 2024-07-30 10:00:26

一起谈.NET技术,谈谈我处理异常的一般方法的相关文章

谈谈我处理异常的一般方法

我们在编写程序的时候会遇到各种各样的意外情况,如除数为0,数组越界,非法转型,栈溢出等等.因而我们需要有一种机制来处理这些情况,异常处理就是其中的一种机制.当然,还有其他的机制,在MFC中,由于标准的不统一,就存在着各种错误报告方法,如有通过函数返回特殊值的方式,有通过执行某一语句后查询特殊语句获取错误的码的方式,等等. 在C#中,只有一种报告方式,即异常.这样可以让开发人员从大量的文档中解脱出来,不必为一些非逻辑的问题而花费大量时间. C#中的异常不同于C++,所有的异常类型都是继承自Syst

一起谈.NET技术,.NET动态调用DLL的方法

很多软件都是可插拔的,最知名的便是微软的Windows操作系统.你可以在Windows操作系统上安装QQ,也可卸掉QQ,这便是可插拔.这里不谈Windows的实现,因为太过复杂.本文就谈谈管理软件的可插拔的实现.相对Windows操作系统,QQ就是它的一个插件.所以可以简单的将开发可插拔的软件分为两个部分.一个是主应用程序的开发,一个是插件的开发. 比Windows小的,常见的可插拔的软件是MSN.MSN主应用程序由MS开发,还存在一些MSN插件开发商,国内好像也有不少,这些插件开发商通过在插件

一起谈.NET技术,Linq To SQL 批量更新方法汇总

方法一.官方例子 地球人都知道的,也是不少 Linq To SQL 反对者认为效率低下的一种方法. NorthwindDataContext db = new NorthwindDataContext(); var customers = db.Customers.Where(c => c.CustomerID.StartsWith("BL")); foreach (var customer in customers) { customer.Address = "Gua

《创业家》牛文文:少谈点模式多谈点技术

"模式"如同当年的"主义",流行于各种创业大赛.创业励志节目.论坛的"街头"式秀场 文/创业家 牛文文 "美国某某公司你知道吧?就是刚被戴尔.惠普.思科十几亿美元抢购的那家.我们的模式和它的一样,现在还没赢利,可将来起码有十几亿人民币的市值." "我开了小煤矿,但煤运不出去,上商学院之后受到启发,想搞模式创新,具体讲就是想在铁路边上搞个煤炭物流开发区,建一个大的物流和信息流平台,把分散的煤炭集中在我这个园区,这样和铁

CSS开发框架技术OOCSS编写和管理CSS的方法

文章简介:今天最流行的CSS开发框架技术当属OOCSS,尽管还有其他类似的技术存在,如BEM.这些方法试图对CSS采用面向对象的编程原则.尽管样式语言和面向对象的软件设计原则在概念之间存在一定的问题,这些微妙的东西对于一个欠缺经验的开发人员来说可能不会立即显现出来.最令人不 公认的拥有一个编写和管理CSS的方法比什么都要更好.尽管如此,一些开发人员的实践是不利于语义化质量和长期的可维护性.我们要讨论一些被提倡的"CSS框架方法"的问题和作为Web开发人员,我们如何可以更好的解决这些问题

.NET程序调试技巧(一):快速定位异常的一些方法

  这篇文章主要介绍了.NET程序调试技巧(一):快速定位异常的一些方法,本文讲解了定位本机异常.在客户环境定位.net程序异常两方面的内容,需要的朋友可以参考下 作为一个程序员,解BUG是我们工作中常做的工作,甚至可以说解决问题能力是一个人工作能力的重要体现.因为这体现了一个程序员的技术水平.技术深度.经验等等. 那么在我们解决BUG的过程中,定位问题是非常重要的.有句话叫"发现问题是解决问题的一半. 本文讲述就快速定位异常(专指.NET程序异常)的方法.包括在本机定位异常,在客户环境定位.n

浅谈html转义及防止javascript注入攻击的方法_javascript技巧

有的时候页面中会有一个输入框,用户输入内容后会显示在页面中,类似于网页聊天应用.如果用户输入了一段js脚本,比例:<script>alert('test');</script>,页面会弹出一个对话框,或者输入的脚本中有改变页面js变量的代码则会时程序异常或者达到跳过某种验证的目的.那如何防止这种恶意的js脚本攻击呢?通过html转义能解决这个问题. 一:什么是html转义? html转义是将特殊字符或html标签转换为与之对应的字符.如:< 会转义为 <> 或转义

thinkPHP线上自动加载异常与修复方法实例分析_php实例

本文实例讲述了thinkPHP线上自动加载异常与修复方法.分享给大家供大家参考,具体如下: 项目遇到一个奇怪的问题,本地代码正常,服务器上却不正常. 经过测试,应该是自动加载出了问题,尝试了各种方法, 1.手动加载,发现好麻烦,没完没了. 2.自己写自动加载,写不出来,尴尬. 3.修改配置,使其支持自动加载,发现还是不行. 后来进行调试, 发现本地支持 import('@.ORG.OSS\OssClient'); import('@.ORG.OSS\Core\OssUtil'); 而服务器上,不

中断和异常的转移方法

80386实模式下的中断和异常的转移方法与8086相同.这里介绍的中断和异常的转移方法是指 80386在保护模式下响应中断和处理异常时所采用的转移方法. 1.中断描述符表IDT 与8086/8088一样,在响应中断或者处理异常时,80386根据中断向量号转对应的处理程序.但是,在保护模式下,80386不使用实模式下的中断向量表,而是使用中断描述符表IDT.在保护模式下,80386把中断向量号作为中断描述符表IDT中描述符的索引,而不再是中断向量表中的中断向量的索引.象全局描述符表GDT一样,在整