4221学习网
首页 | 网址大全 | 脑力倍增 | 电脑学院 | 学习方法 | 口才交际 | 英语学习 | 工作职场 | 成功励志 | 文学小说 | QQ·技巧 | 视频短片 | 视频教程 | NBA | 图片 | 游戏技巧 | 恋爱技巧 | 专题教程 | 4221论坛
热门关键字: 视频教程  美女  百家讲坛  记忆力  疯狂英语
 → 当前位置:4221学习网>电脑学院>编程开发>C#教程>正文

C#学习第十五天

4221学习网 2007-09-27 来源:互联网 收藏本文

第七章 异常处理

通用语言运行时(CLR)具有的一个很大的优势为,异常处理是跨语言被标准化的。一个在C#中所引发的异常可以在Visual Basic客户中得到处理。不再有 HRESULTs 或者 ISupportErrorInfo 接口。尽管跨语言异常处理的覆盖面很广,但这一章完全集中讨论C#异常处理。你稍为改变编译器的溢出处理行为,接着有趣的事情就开始了:你处理了该异常。要增加更多的手段,随后引发你所创建的异常。

7.1 校验(checked)和非校验(unchecked)语句

当你执行运算时,有可能会发生计算结果超出结果变量数据类型的有效范围。这种情况被称为溢出,依据不同的编程语言,你将被以某种方式通知——或者根本就没有被通知。(C++程序员听起来熟悉吗?)

那么,C#如何处理溢出的呢? 要找出其默认行为,请看我在这本书前面提到的阶乘的例子。(为了方便其见,前面的例子再次在清单 7.1 中给出)

清单 7.1 计算一个数的阶乘

7.2  异常处理语句

既然你知道了如何产生一个异常(你会发现更多的方法,相信我),仍然存在如何处理它的问题。如果你是一个 C++ WIN32 程序员,肯定熟悉SEH(结构异常处理)。你将从中找到安慰,C#中的命令几乎是相同的,而且它们也以相似的方式运作。

The following three sections introduce C#'s exception-handling statements:

以下三节介绍了C#的异常处理语句:

。用 try-catch 捕获异常
。用try-finally 清除异常
。用try-catch-finally 处理所有的异常

7.2.1 使用 try 和 catch捕获异常

你肯定会对一件事非常感兴趣——不要提示给用户那令人讨厌的异常消息,以便你的应用程序继续执行。要这样,你必须捕获(处理)该异常。
这样使用的语句是try 和 catch。try包含可能会产生异常的语句,而catch处理一个异常,如果有异常存在的话。清单7.3 用try 和 catch为OverflowException 实现异常处理。

清单7.3 捕获由Factorial Calculation引发的OverflowException 异常

1: using System;
2:
3: class Factorial
4: {
5: public static void Main(string[] args)
6: {
7: long nFactorial = 1, nCurDig=1;
8: long nComputeTo = Int64.Parse(args[0]);
9:
10: try
11: {
12: checked
13: {
14: for (;nCurDig <= nComputeTo; nCurDig++)
15: nFactorial *= nCurDig;
16: }
17: }
18: catch (OverflowException oe)
19: {
20: Console.WriteLine("Computing {0} caused an overflow exception", nComputeTo);
21: return;
22: }
23:
24: Console.WriteLine("{0}! is {1}",nComputeTo, nFactorial);
25: }
26: }

为了说明清楚,我扩展了某些代码段,而且我也保证异常是由checked 语句产生的,甚至当你忘记了编译器设置时。

正如你所见,异常处理并不麻烦。你所有要做的是:在try语句中包含容易产生异常的代码,接着捕获异常,该异常在这个例子中是OverflowException类型。无论一个异常什么时候被引发,在catch段里的代码会注意进行适当的处理。

如果你不事先知道哪一种异常会被预期,而仍然想处于安全状态,简单地忽略异常的类型。

try
{
...
}
catch
{
...
}

但是,通过这个途径,你不能获得对异常对象的访问,而该对象含有重要的出错信息。一般化异常处理代码象这样:

try
{
...
}
catch(System.Exception e)
{
...
}

注意,你不能用ref或out 修饰符传递 e 对象给一个方法,也不能赋给它一个不同的值。

7.2.2 使用 try 和 finally 清除异常

如果你更关心清除而不是错误处理, try 和 finally 会获得你的喜欢。它不仅抑制了出错消息,而且所有包含在 finally 块中的代码在异常被引发后仍然会被执行。尽管程序不正常终止,但你还可以为用户获取一条消息,如清单 7.4 所示。

清单 7.4 在finally 语句中处理异常

1: using System;
2:
3: class Factorial
4: {
5: public static void Main(string[] args)
6: {
7: long nFactorial = 1, nCurDig=1;
8: long nComputeTo = Int64.Parse(args[0]);
9: bool bAllFine = false;
10:
11: try
12: {
13: checked
14: {
15: for (;nCurDig <= nComputeTo; nCurDig++)
16: nFactorial *= nCurDig;
17: }
18: bAllFine = true;
19: }
20: finally
21: {
22: if (!bAllFine)
23: Console.WriteLine("Computing {0} caused an overflow exception", nComputeTo);
24: else
25: Console.WriteLine("{0}! is {1}",nComputeTo, nFactorial);
26: }
27: }
28: }

通过检测该代码,你可能会猜到,即使没有引发异常处理,finally也会被执行。这是真的——在finally中的代码总是会被执行的,不管是否具有异常条件。为了举例说明如何在两种情况下提供一些有意义的信息给用户, 我引进了新变量bAllFine。bAllFine告诉finally 语段,它是否是因为一个异常或者仅是因为计算的顺利完成而被调用。

作为一个习惯了SEH程序员,你可能会想,是否有一个与__leave 语句等价的语句,该语句在C++中很管用。如果你还不了解,在C++中的__leave 语句是用来提前终止 try 语段中的执行代码,并立即跳转到finally 语段 。坏消息, C# 中没有__leave 语句。但是,在清单 7.5 中的代码演示了一个你可以实现的方案。

清单 7.5 从 try语句 跳转到finally 语句

1: using System;
2:
3: class JumpTest
4: {
5: public static void Main()
6: {
7: try
8: {
9: Console.WriteLine("try");
10: goto __leave;
11: }
12: finally
13: {
14: Console.WriteLine("finally");
15: }
16:
17: __leave:
18: Console.WriteLine("__leave");
19: }
20: }

当这个应用程序运行时,输出结果为

try
finally
__leave

一个 goto 语句不能退出 一个finally 语段。甚至把 goto 语句放在 try 语句 段中,还是会立即返回控制到 finally 语段。因此,goto 只是离开了 try 语段并跳转到finally 语段。直到 finally 中的代码完成运行后,才能到达__leave 标签。按这种方式,你可以模仿在SEH中使用的的__leave 语句。

顺便地,你可能怀疑goto 语句被忽略了,因为它是try 语句中的最后一条语句,并且控制自动地转移到了 finally 。为了证明不是这样,试把goto 语句放到Console.WriteLine 方法调用之前。尽管由于不可到达代码你得到了编译器的警告,但是你将看到goto语句实际上被执行了,且没有为 try 字符串产生的输出。

1: using System;
2:
3: class CatchIT
4: {
5: public static void Main()
6: {
7: try
8: {
9: int nTheZero = 0;
10: int nResult = 10 / nTheZero;
11: }
12: catch(DivideByZeroException divEx)
13: {
14: Console.WriteLine("divide by zero occurred!");
15: }
16: catch(Exception Ex)
17: {
18: Console.WriteLine("some other exception");
19: }
20: finally
21: {
22: }
23: }
24: }

这个例子的技巧为,它包含了多个catch 语句。第一个捕获了更可能出现的DivideByZeroException异常,而第二个catch语句通过捕获普通异常处理了所有剩下来的异常。

你肯定总是首先捕获特定的异常,接着是普通的异常。如果你不按这个顺序捕获异常,会发生什么事呢?清单7.7中的代码有说明。

清单7.7 顺序不适当的 catch 语句

1: try
2: {
3: int nTheZero = 0;
4: int nResult = 10 / nTheZero;
5: }
6: catch(Exception Ex)
7: {
8: Console.WriteLine("exception " + Ex.ToString());
9: }
10: catch(DivideByZeroException divEx)
11: {
12: Console.WriteLine("never going to see that");
13: }

编译器将捕获到一个小错误,并类似这样报告该错误:
wrongcatch.cs(10,9): error CS0160: A previous catch clause already
catches all exceptions of this or a super type ('System.Exception')

最后,我必须告发CLR异常与SEH相比时的一个缺点(或差别):没有 EXCEPTION_CONTINUE_EXECUTION标识符的等价物,它在SEH异常过滤器中很有用。基本上,EXCEPTION_CONTINUE_EXECUTION 允许你重新执行负责异常的代码片段。在重新执行之前,你有机会更改变量等。我个人特别喜欢的技术为,使用访问违例异常,按需要实施内存分配。


1: using System;
2: 
3: class Factorial
4: {
5: public static void Main(string[] args)
6: {
7: long nFactorial = 1;
8: long nComputeTo = Int64.Parse(args[0]);
9: 
10: long nCurDig = 1;
11: for (nCurDig=1;nCurDig <= nComputeTo; nCurDig++)
12: nFactorial *= nCurDig;
13: 
14: Console.WriteLine("{0}! is {1}",nComputeTo, nFactorial);
15: }
16: }


当你象这样使用命令行执行程序时

factorial 2000

结果为0,什么也没有发生。因此,设想C#默默地处理溢出情况而不明确地警告你是安全的。
通过给整个应用程序(经编译器开关)或于语句级允许溢出校验,你就可以改变这种行为。以下两节分别解决一种方案。

7.1.1 给溢出校验设置编译器

如果你想给整个应用程序控制溢出校验,C#编译器设置选择是正是你所要找的。默认地,溢出校验是禁用的。要明确地要求它,运行以下编译器命令:


csc factorial.cs /checked+


现在当你用2000参数执行应用程序时,CLR通知你溢出异常。

按OK键离开对话框揭示了异常信息:


Exception occurred: System.OverflowException
at Factorial.Main(System.String[])


现在你了解了溢出条件引发了一个 System.OverflowException异常。下一节,在我们完成语法校验之后,如何捕获并处理所出现的异常?

7.1.2 语法溢出校验

如果你不想给整个应用程序允许溢出校验,仅给某些代码段允许校验,你可能会很舒适。对于这种场合,你可能象清单7.2中显示的那样,使用校验语句。

清单 7.2  阶乘计算中的溢出校验


1: using System;
2: 
3: class Factorial
4: {
5: public static void Main(string[] args)
6: {
7: long nFactorial = 1;
8: long nComputeTo = Int64.Parse(args[0]);
9: 
10: long nCurDig = 1;
11: 
12: for (nCurDig=1;nCurDig <= nComputeTo; nCurDig++)
13: checked { nFactorial *= nCurDig; }
14: 
15: Console.WriteLine("{0}! is {1}",nComputeTo, nFactorial);
16: }
17: }


甚至就如你运用标志 checked-编译了该代码,在第13行中,溢出校验仍然会对乘法实现检查。错误信息保持一致。

显示相反行为的语句是非校验(unchecked )。甚至如果允许了溢出校验(给编译器加上checked+标志),被unchecked 语句所括住的代码也将不会引发溢出异常:


unchecked
{
nFactorial *= nCurDig;
}

上一篇:C#学习第十六天   下一篇:C#学习第十四天
添加到google书签 digg this! 添加到bolaa 添加到yahoo+ 添加到新浪vivi 添加到365key  
收藏】 【评论】 【推荐】 【投稿】 【打印】 【关闭

相关文章
·C#学习第十六天
·C#学习第十四天
·C#学习第十三天
·C#学习第十二天
·C#学习第十一天
·C#学习第十天
·C#学习第九天
·C#学习第八天
·C#学习第七天
·C#学习第六天
发表评论
要记得去论坛讨论,点击注册新会员) 密码: 匿名评论
评论内容:(请自觉遵守互联网相关政策法规。)
最新文章
·C#学习第一天
·C#学习第二天
·C#学习第三天
·C#学习第四天
·C#学习第五天
·C#学习第六天
·C#学习第七天
·C#学习第八天
·C#学习第九天
·C#学习第十天
本类阅读排行榜
·学习极限 挑战C#学习的最快速度
·再现C#导出Excel源码
·C#学习第一天
·解读C#中的规则表达式
·C#连接mysql
·操作SQL Server Mobile 2005数据库的常用
·C#学习第十一天
·C#学习第八天
·C#学习第七天
·C#学习第二天
热点视频教程
视频街舞 舞步 教学
视频李孝利十分钟详细舞蹈教程
视频windowsxp重装系统视频教程
视频美女教你跳舞
视频街舞 舞步 教学2
视频韩国的太空步教程,后滑、侧滑、旋转太空
视频如何安装双操作系统
视频台球教程-基本杆法
视频双截棍视频教程-定式
视频【WindowsXP入门教程】 - 硬盘分区
视频启功书法视频
视频24式太极拳教学---基本动作
视频[百家讲坛]三十六计01_借刀杀人
视频 斯诺克台球竿法-后退球
视频超级全脑速读训练教程

设为首页 - 加入收藏 - 关于我们 - 联系我们 - 友情连接

4221学习网版权所有-鄂ICP备07006816号
已浏览: