第7讲:优化.NET异常处理

2005.1.11 欧岩亮

课程介绍

我们一起来讨论.NET中处理异常的策略,看看处理异常的最好方法是什么

 

基础内容

理解如何使用Microsoft .NET Framework进行编程

VS.NET使用

了解C#或VB.NET

 

课程内容

设计异常处理

.NET中基本的异常处理语法

异常的优先级

如何抛出异常

最佳的异常处理方法

异常处理的应用程序块(Application Block)

 

理解异常是一定会发生的

大多数的软件系统都不是百分之百可靠的!

要站在异常一定可能会发生的角度来编写异常处理程序,应对程序有可能发生的错误。

建立一个良好的异常处理策略。

 

基本的异常处理语法

image

所有的异常都是从Exception派生的。

 

演示一

异常处理

image
制造一个除0异常

image

image

 

Catch块的优先性

多个Catch块的情况下,Catch块能够过滤异常,我们应该先截获具体的异常,再截获一般的异常。在写异常捕获的时候,捕获异常的顺序很重要,如果异常已经被某个Catch捕获了,那么后面的Catch就不会再处理该异常了。

image

 

抛出异常

抛出异常是为了告诉程序调用者,程序产生的错误结果是由于他们的调用引起的。

例如:一个不可能实现的接口可能会抛出一个"NotSupportedException"的异常。

使用通用的Framework异常:在开发过程中,可能会抛出"NotImplementedException"的异常。

还可以从系统异常派生出自己的具体异常,为具体的应用程序所使用。

一般派生自己异常的时候,推荐派生自System.ApplicationException这个类,这指的是一个系统级别的异常,所以我们编写应用程序的时候应该遵循的一个规则。

 

抛出异常的语法

image

 

演示二

抛出异常

image

增加一个捕获异常的Catch块

image

运行结果

image

如果是我们主动抛出异常,那么我们最好抛出具体的一个异常,而不是只抛出Exception类型的异常。这样能方面我们捕获对应类型的异常。

 

处理未预料的异常

确保所有的程序入口都使用了try-catch

在catch中截获所有的异常

异常处理技术

1.记录异常

在文件中记录异常

在数据库中记录异常

在Eventlog中记录异常

2.发送E-mail来通知异常

3.异常产生是,用友好(user-friendly)的方式通知用户

 

演示三

处理未预料的异常

image

非授权访问的异常。这里默认是选中第二个按钮

image

IO的异常

image

基本异常,捕获基本异常也可以只写catch,不写后面的Exception内容。这样也是能捕获所有异常的,但是它不能够具体地知道异常的内容。

image

Finally,鼠标指针还原

image

运行结果

 

添加一个全局异常处理函数

我们虽然没有在所有的应用程序入口写捕获异常的try-catch块,但是使用Application对象中的ThreadException属性可以设置一个delegate来捕获所有未处理的Main UI线程中出现的异常

image

这种方式只能处理主线程当中的异常,其他工作线程、辅助线程在异常时捕获不到的。

 

演示四

全局异常处理

image

image

ServiceNotification是指当前的对话框会置于所有程序的最前方。

image

我们在这里直接抛出异常,并且不用try-catch包装

image

这里全局异常处理就会截获到这个异常。

 

工作线程(Worker Threads)中的异常

编写多线程代码时,必须考虑在工作线程中出现的异常

在线程的入口使用try-catch

使用delegate或其他的方式将发生的异常通知给主线程

 

演示五

工作线程中的异常

image

创建新线程

image

image

非主线程中抛出异常,我们使用BeginInvoke来通知主线程。

在多线程之间互操作的时候,我们的工作线程如果想要访问主线程里面的界面元素,我们必须通过BeginInvoke来访问。也就是说,属于哪一个线程的界面元素,必须由哪一个线程来进行访问。我们必须去调用对应界面元素线程中的BeginInvoke方法,来访问它自己的界面元素。

在EmulateLongProcess执行在另外一个线程内部,如果这个方法里面发生了异常,那么catch就会捕获到这个异常,这个线程去调用BeginInvoke方法。这里的BeginInvoke方法一定是this.BeginInvoke。这里的this一定是当前的frmCustomer。

image

frmCustomer将完成BeginInvoke方法,让当前这个form去执行WorkerThreadExceptionHandler方法。我们通过一个delegate方式来执行。

image

这里WorkerThreadExceptionHandler里面又调用了一个主线程异常的delegate,把异常抛出给主线程。

我们不能直接在catch块中调用WorkerThreadExceptionHandler方法,因为这个方法里面需要访问主线程的界面元素,因此我们必须使用BeginInvoke方法来让主线程自己去调用这个方法。

当然这里我们可以不用this.BeginInvoke,而是用this.TextBox1.BeginInvoke。因为这里BeginInvoke最根本的含义是让主线程来完成这个方法。

image

运行结果,异常被正确地捕获到

image

这里如果我们把工作线程调用函数的try-catch块去掉,并在里面抛出异常

image

运行结果,异常一定是发生了,而界面程序没有任何反应,这样用户就会很诧异。

image

这就是说,如果工作线程不去做异常处理,那么这个异常就会消失了,将没有人会了解这个异常究竟是什么。

 

异常处理的最好方法

不要:

Catch异常并re-throw

因为重新抛出一个新的异常,会损失一些消息,例如会损失这个异常中带有的一些调用堆栈的信息。

通过抛出异常来控制代码的执行。

在程序的构造函数入口处添加try-catch方法/属性/构造

MessageBox.Show(MyException.ToString())

使用了try-catch但是并没有处理异常

 

需要:

从始至终要紧记异常处理的策略

在应用程序所有的入口处使用try-catch:事件处理函数,主函数,线程入口

处理所有意料到的异常

编写代码时要注意考虑到应用程序最差的情况

显示有好的信息,并提供适当的管理员联系信息

在可能的情况下提供可能的选择(终止,重试,忽略)

 

异常处理程序块

Publisher/subscriber设计模式

下载异常处理程序块

http://www.microsoft.com/downloads/details.aspx?FamilyID=8ca8eb6e-6f4a-43df-adeb-8f22ca173e02&DisplayLang=en

编译工程

在新的工程中添加引用

引入名称空间Microsoft.ApplicationBlock.ExceptionManagement

使用ExceptionManager.Publish()来发布异常

配置app.config文件启用异常管理

在.config文件中配置一些信息可以添加自己的异常处理模块

 

配置异常处理应用程序块

image

 

演示六

异常处理程序块

image

首先加入两个已有的微软提供的异常项目,并添加引用

image

然后在主线程异常中放置ExceptionManager的Publish方法,并传入异常信息,这样我们就能够把这个异常信息公布出来,公布的具体位置写在config文件中。

image

配置文件中首先定义了一个配置区域section,名叫exceptionManagement。下面就有一个exceptionManagement配置块。它的属性mode设置为on表示下面所有配置publisher的功能全部启用。logname是在日志管理里面日志的名称。applicationname是日志中记录的应用程序名称。

当出现了异常之后,我们同时也公布了这个异常。

image

打开Administrative Tools的Event Viewer

image

能看见我们这个异常日志

image

image

 

总结

接受现实:异常是一定会发生的

定制一套异常处理的策略

不能盲目的来处理异常

.NET中的结构化异常处理是一个强大有效地工具

2010.10.10

你可能感兴趣的:(.net)