1、异常处理的总体指导思想  

  学习C#中的异常处理机制,大概要了解以下几点:

  • 首先,我们需要知道的事所有具体异常都是继承自System.Exception基类的。
  • 其次,要熟悉FCL类库内置好的一些异常,例如:FileNotFoundException。
  • 最后,不仅要懂得catch异常还要懂得throw异常

  在实际动手写异常处理构造之前,首先要大概猜测一下可能会出现什么异常,既然可能发生异常,就意味着肯定存在着不稳定的因素。例如文件IO、网络IO、数据库操作、跟其他程序的交互(包括跟操作系统的交互)等等。由于.NET平台本身的安全设计,那些仅需要CPU、内存的操作往往不大可能出现异常,例如字符串处理操作。如果连这类操作都出现异常说明CPU或内存出现问题了,那么这是.NET平台能不能正常运行还是一回事呢,在应用中处理异常也就多半没什么用了。

  那么为何要catch异常呢?主要有几个方面:对用户友好、隐藏敏感信息(比如数据库连接字符串)、增强稳定性(比如对于某些不稳定的操作catch之后再重试几次)、记录日志以便在不能调试的时候获取异常信息。 

  其实,大部分开发框架中都内置了最顶层的异常处理事件,例如,WinForm中有Application.ThreadException事件、MVC中可以通过Fitler机制实现,这样的话代码中除非要特殊处理才catch,那些尚未预计到的bug就可以通过异常处理事件捕获,不用到处写try...catch。

  在catch之后还要懂的throw异常。考虑下面两种情形:

  • 考虑输入验证的场景,例如用户输入的email格式不对,这时底层抛出的异常直接传递给顶层的调用者(即和用户交互的UI层),这样调用者可以拿这些异常信息决定如何告知用户。
  • 在向顶层抛到过程中,catch异常之后继续抛。之所以要catch也许是因为又发生了其他异常,然后包装一下将它们都抛出去;也许是需要记录一下日志。

2、实例讲解异常处理

  下面让我们以本文的一个例子及《.NET中的异常处理机制(二)》中的一个例子来说明如何处理异常。

  为什么要处理异常呢?让我们从一个简单的例子看。从一个文本文件读取数据然后打印到控制台上。

 public sealed class Program
{
public static void Main()
{
StreamReader streamReader = new StreamReader(@"c:\data.txt");
Console.WriteLine(streamReader.ReadToEnd());
streamReader.Close();
}

  如上图的代码,如果不处理异常,那么程序在运行时就会抛出一个FileNotFoundException,然后跟着一大堆该异常的相关信息。这些异常信息对于开发人员也许不是什么问题,但是对于没有.NET相关背景知识的终端用户来说,这就是大麻烦了,因为用户不理解这些信息,这些信息对他们毫无意义。此外,这些异常信息包含着一些有价值的信息,它也许能被黑客利用然后侵入你的应用。最后,从应用的角度来说,如果抛出的异常未被处理,那么程序中抛出异常的程序下面的代码就不会被执行,这导致一些资源不能被释放。拿本例来说,若在读取data.text文本文件时抛出异常并且未被处理,那么streamReader,Close()就不会被执行。

  向终端用户(end user)显示未处理的异常是不好的做法,有以下两方面的原因:

  • 异常信息对用户来说通常是晦涩难懂的,实际意义不大
  • 异常中包含一些信息,能被其他人用来hack进你的应用

  异常实际上是一个继承自System.Exception类的具体子类。System.Exception基类提供了几个有用的属性,这些属性包含了异常的丰富信息。比如,message属性和Stack Trace属性。

  现在,让我们用C#提供的构造来捕捉异常。

try
{
StreamReader streamReader = new StreamReader(@"c:\data1.txt");
Console.WriteLine(streamReader.ReadToEnd());
streamReader.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.Source);
Console.WriteLine(ex.TargetSite);
}

  现在,通过try...catch我们捕捉到了异常,但是由于我们传给catch的是基类异常Exception,那么,我们仅能使用几个属性,由于我们已经知道抛出的是FileNotFoundException,故我们可以进行改进,将Exception改为FileNotFoundException,这样的话,我们在Catch语句块中处理异常时可以利用FileNotFoundException的更多属性(比如FileName属性)来提供更加有意义的异常处理程序。比如,我们将异常的Message和Stack Trace属性记录到日志文件或者数据库为了调试目的。在这之后还需要向用户显示一个有意义的信息。注意这里基类Exception提供的属性被我们拿来做实际的异常处理(即记录备案,方便稍后调试),在这之后我们可以通过具体异常子类(比如FileNotFoundException)提供的更多属性来向用户显示更加有意义的信息。如下面的代码所示。

 try
{
StreamReader streamReader = new StreamReader(@"c:\data1.txt");
Console.WriteLine(streamReader.ReadToEnd());
streamReader.Close();
}
catch (FileNotFoundException ex)
{
//这里将异常的详细信息记录到数据库或日志文件
Console.WriteLine("请检查文件{0}是否存在",ex.FileName);
}

  现在让我们将上面的代码做一点变化,如下图所示,

 try
{
StreamReader streamReader = new StreamReader(@"c:\Test_File\Data1.txt");
Console.WriteLine(streamReader.ReadToEnd());
streamReader.Close();
}
catch (FileNotFoundException ex)
{
//这里将异常的详细信息记录到数据库或日志文件
Console.WriteLine("请检查文件{0}是否存在",ex.FileName);
}

  这里我们添加上了Test_File路径名,但实际上该目录不存在,那么这时就会抛出DirectoryNotFoundException,由于没有对应的处理程序,那么程序就会终止,由于我们在开发中不可能记住所有的抛出的具体异常的类型名,因此,在这里我们可以这样处理,在FileNotFoundException异常的catch块之后添加捕获基类Exception异常的catch块,这样的话,如果有我们预料之外的任何异常都将在最后一个捕获基类Exception异常的catch块中得到处理。这里,需要注意的是所有的捕获具体异常类型的catch块必须位于捕获基类Exception异常的catch块之前,否则C#编译器将报错。这里最后一个捕获基类Exception异常的catch块之所以能捕获任意子类型异常,正是体现了继承的思想:父类型的引用能指向任何子类型的实例对象。

 try
{
StreamReader streamReader = new StreamReader(@"c:\Test_File\Data1.txt");
Console.WriteLine(streamReader.ReadToEnd());
streamReader.Close();
}
catch (FileNotFoundException ex)
{
//这里将异常的详细信息记录到数据库或日志文件
Console.WriteLine("请检查文件{0}是否存在",ex.FileName);
}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}

  让我们来看看为什么需要finally块。上面代码中,如果try块抛出异常,那么streamReader就不能正常关闭,进而造成资源得不到释放。这时候finally块就派上用场了,finally块中的语句无论是否抛出异常均保证得到执行。因此,我们常将一些资源清理的工作放在finally块中。本例中,我们可以将streamReader.Close()放在finally块中。

  另外,常有人质疑finally块的作用。他们的理由就是可以将资源清理的工作放在最后一个catch语句块之后,因为无论try块中是否发生异常,最后一个catch块之后的程序代码均会被执行。这种说法是错误的!因为当try块捕获到异常,转到catch块中进行处理时,若在catch块中再次抛出异常且此异常没有设置对应的处理程序,那么程序就会终止,不会执行最后一个catch块之后的任何程序代码。

.NET中的异常处理机制(一)的更多相关文章

  1. 【C++】异常简述(一):C语言中的异常处理机制

    人的一生会遇到很多大起大落,尤其是程序员. 程序员写好的程序,论其消亡形式无非三种:无疾而终.自杀.他杀. 当然作为一名程序员,最乐意看到自己写的程序能够无疾而终,因此尽快的学习异常处理机制是非常重要 ...

  2. C++中的异常处理机制

    C++中的捕获异常机制catch参数中实参的类型不同,采取的处理方式则不相同,且与普通的函数调用还不一样,具体表现为当抛出异常throw A()或throw obj时,对象会进行一次额外的对象复制操作 ...

  3. 16、java中的异常处理机制

    异常:就是程序在运行时出现不正常情况.异常由来:问题也是现实生活中一个具体的事物,也可以通过java的类的形式进行描述.并封装成对象. 其实就是java对不正常情况进行描述后的对象体现. 对于问题的划 ...

  4. java中的异常处理机制

    java异常处理机制 1)在java语言中,通常将可能出现异常的语句放入try{}语句中,将出现错误后需要执行的语句放入到catch{}语句中,将无论是否发生异常都要执行的语句放在finally{}语 ...

  5. 深入理解C++中的异常处理机制

    异常处理 增强错误恢复能力是提高代码健壮性的最有力的途径之一,C语言中采用的错误处理方法被认为是紧耦合的,函数的使用者必须在非常靠近函数调用的地方编 写错误处理代码,这样会使得其变得笨拙和难以使用.C ...

  6. Java 中的异常处理机制

    生活中的异常:  不能够完整而顺利的完成一些工作 根据不同的异常进行相应的处理,而不会就此终端我们的生活 引出:  异常处理: 方式:  1.选择结构(逻辑判断)避免 demo:if逻辑处理异常 im ...

  7. C语言中的异常处理机制

    #define try if(!setjmp(Jump_Buffer)) 返回try现场后重新执行判断,所以有两次执行. http://blog.csdn.net/tian_dao_chou_qin/ ...

  8. Java中的异常处理机制《》

    异常机制已经成为判断一门编程语言是否成熟的标准,异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性. Java异常机制主要依赖于try.catch.finall ...

  9. Java中的异常处理机制的简单原理和应用?

    程序运行过程中可能出现各种"非预期"情况,这些非预期情况可能导致程序非正常结束. 为了提高程序的健壮性,Java提供了异常处理机制: try { s1... s2... s3... ...

随机推荐

  1. django单表操作,增、删、改、查

    一.实现:增.删.改.查 1.获取所有数据显示在页面上 model.Classes.object.all(),拿到数据后,渲染给前端;前端通过for循环的方式,取出数据. 目的:通过classes(班 ...

  2. Git---时光穿梭机之版本回退02

    现在你的本地仓库底下添加一个readme.txt文件 第一次readme.txt的内容如下:: Git is a version control systemGit is free sofwore 然 ...

  3. Luajit-2.1.0-beta1的发布和生成arm64用bytecode的解脱

    前情提要:由于苹果要求2015年2月1日上架的新app必须支持64位的arm64,旧的app也得在6月1日支持64位,来源.于是unity3d弄出了il2cpp这种花式的玩法来进行64位支持,而对于当 ...

  4. 前端开发之jQuery效果篇

    主要内容: 1.显示与隐藏效果 2.滑动效果 3.淡入与淡出效果 4.动画效果 5.弹出广告效果 一.显示与隐藏 显示与隐藏即 show() 和 hide() ,能够控制元素显示或隐藏. 实例: &l ...

  5. CompletionPort

    [CompletionPort] 1.CreateIoCompletionPort.此方法用于创建与绑定.此方法调用后,socket即退出,只能通过CompletionKey来辨认是哪个socket的 ...

  6. gRPC初探——概念介绍以及如何构建一个简单的gRPC服务

    目录 引言 1. gRPC简介 2. 使用Protocol Buffers进行服务定义 2.1 定义消息 2.2 定义服务接口 3.构建简单的gRPC服务 3.1 编写proto文件,定义消息和接口 ...

  7. 磁盘存储结构与文件恢复实验(FAT文件系统)

    实验地点:主楼A2-412 一.实验室名称:主楼实验室A2-412                  二.实验项目名称:磁盘存储结构与文件恢复实验 三.实验学时:6学时 四.实验原理: 在Debug环 ...

  8. 47. Permutations II (Back-Track, Sort)

    Given a collection of numbers that might contain duplicates, return all possible unique permutations ...

  9. 运行Junit单测时遇到的问题

    现在有两个办法解决: 1.junit版本降到4.10 2.导入hamcrest-core-1.3.jar 官网:JUnit now uses the latest version of Hamcres ...

  10. icp算法

    https://github.com/tttamaki/ICP-test https://github.com/tttamaki/SICP-test