异常是指成员没有完成它的名称所宣称的行动。
如 FileStream 的 方法里有 Read,Write,等等(行动成员通常用动词表示)。当行动成员不能完成任务时,就应抛出异常。
try:
如果代码需要执行一般性的资源清理操作,需要从异常中恢复,或者两者都需要,就可以放到 try 块中。负责清理的代码应该放到一个 finally 块中。try 块还可包含也许会抛出异常的代码。负责异常恢复的代码应放到一个或多个 catch 块中。针对应用程序能从中安全恢复的每一种异常,都应该创建一个 catch 块。一个 try 块至少要有一个关联的 catch 块或 finally 块,单独一个 try 块没有意义,C# 也不允许。
开发人员有时不知道应该在一个 try 块中放入多少代码。这具体取决于状态管理。如果在一个 try 块中执行多个可能抛出同一个异常类型的操作,但不同的操作有不同的异常恢复措施,就应该将每个操作都放到它自己的 try 块中,这样才能正确地恢复状态。
catch:
catch 块包含的是响应一个异常需要执行的代码。一个 try 块 可以关联0个或多个 catch 块。如果try 块中的代码没有造成异常的抛出,CLR 永远不会执行它的 catch块。线程将跳过所有 catch 块,直接直观性 finally 块(如果有的话)。
catch 关键字后的圆括号中的表达式称为捕捉类型。c# 要求捕捉类型必须是 System.Exception 或者它的派生类型。例如处理 InvalidOperationException 异常和 IOException 的 catch 块。最后一个 catch 块没有指定捕捉类型,能处理除了前面的 catch块指定的之外的其他所有异常;这相当于捕捉 System.Exception。
CLR 自上而下搜索匹配的 catch 块,所以应该将较具体的异常放在顶部。也就是说,首先出现的是派生程度最大的异常类型,接着是它们的基类型(如果有的话),最后是System.Exception。
在 try 块的代码(或者从 try 块调用的任何方法)中抛出异常,CLR 将搜索捕捉类型与抛出的异常相同(或者是它的基类)的 catch 块。如果没有任何捕捉类型与抛出的异常匹配,CLR会去调用栈更高的一层搜索与异常匹配的捕捉类型。如果都到了调用栈的顶部,还是没有找到匹配的 catch 块就会发生未处理的异常。一旦 CRL 找到匹配的 catch 块,就会执行“内层”所有的 finally 块中的代码。所谓“内层 finally 块” 是指从抛出异常的 try 块开始,到匹配异常的 catch 块之间所有的 finally 块。注意,匹配异常的那个 catch 块所关联的 finally 块尚未执行,该 finally 块中的代码一直要等到这个 catch 块中的代码执行完毕才执行。
static void Main(string[] args) { /* 嵌套try块 * try * { * //A * try * { * //B * } * catch * { * //C * } * finally * { * //D * } * //E * } * catch * { ... } * finally * { ... } * * 抛出异常在:内层A,E处由外层catch块捕获,并执行外层finally * 抛出异常在:内层B处,且有一合适内层catch捕获,执行内层finally,后执行E处 * 抛出异常在:内层B处,但内层catch块没有合适处理程序,执行内层finally,搜索外层catch,找合适的,执行外层finally,此时不会执行E * 抛出异常在:内层C处,退出内层catch块,执行内层finally,搜索外层catch,找到合适,执行外层finally * 抛出异常在:内层D处,退出内层finally块,搜索外层catch,找到合适,执行外层finally */ /* 使用嵌套块的原因: * 1.修改所抛出的异常类型 * 2.在代码的不同地方处理不同类型的异常 */ }
以上代码转至:C#嵌套try块工作原理
所有内层 finally 块执行完毕后,匹配异常的那个 catch 块中的代码才开始执行。catch 块中的代码通常执行一些对异常进行处理的操作。在 catch 块的末尾,我们有以下三个选择。
1、重新抛出相同的异常,向调用栈高一层的代码通知该异常的发生。
2、抛出一个不同的异常,向调用栈高一层的代码提供更丰富的异常信息。
3、让现成从 catch 块的底部退出。(非终止线程,而是说执行正常地“贯穿” catch 块的底部,并执行匹配的 finally 块)
选择前两种技术将抛出异常,CLR 的行为和之前说的一样:回溯调用栈,查找捕捉类型与抛出的异常的类型匹配的 catch 块。
选择后一种技术,当线程从 catch 块的底部退出后,它将立即执行包含在 finally 块(这个才是与 catch 关联的 finally 块,也就是常说的 try-catch-finally 中的finally)中的代码。finally 块的所有代码执行完毕后,线程退出 finally 块,执行紧跟 finall 块之后的语句。如果不存在 finally 块,线程将从最后一个 catch 块之后的语句开始执行。
c# 允许在捕捉类型后置顶一个变量。如 ArgumentNullException e,可通过 e 查看异常的具体信息。虽然这个对象可以修改,但最好不要这么做,而应把它当成只读。