关于 try..catch..finally

那天中午和同事讨论起c#中的try..catch..finally. 据说有人分析IL发现这块的运行逻辑和原来C++中是不同的( 注1 ). 于是这个原本很清晰的问题在我头脑中又模糊起来. 本着实践出真知的原则, 我特意写了一小段代码调查了一下, 现将结果拿出来与大家分享.

 1     class Program {
 2         static void Main(string[] args) {
 3             try {
 4                 int i = new Program().Test();
 5                 Console.WriteLine(i);
 6             }
 7             catch { }
 8         }
 9 
10         public int Test() {
11             int i = 0;
12             try {
13                 i = int.Parse("2");
14                 return i;
15             }
16             catch {
17                 i = -1;
18                 throw new Exception(i.ToString());
19             }
20             finally {
21                 i = 1;
22             }
23             return i;
24         }
25     }

运行代码,  发现Test函数中是这样运行的:

1. 行号: 11 -> 12 -> 13 -> 14 -> 20 -> 21 -> 22 -> 24.
最终返回结果为2. 也就是说当try块中的代码全部运行完之后, 又跳转到finally块中运行, 完成后直接返回try块中的值.

2. 将上述代码中第14行的 " return i; " 注释掉, 运行顺序变成: 11 -> 12 -> 13 -> 20 -> 21 -> 22 -> 23 -> 24.
最终返回结果为1. 也是先运行完整个try块, 再运行finally块, 最后return.

3. 最后将上述代码第13行改成 " i = int.Parse("2a"); " 制造一个异常. 发现运行顺序变成: 11 -> 12 ->13 -> 16 -> 17 -> 18 -> 20 -> 21 -> 22.
最终抛出异常. 这说明程序是先运行try块, 发现异常后运行catch块, 在离开catch块之前先运行finally块. 然后直接抛出18行的异常.

综合上述三种情况, 我发现其实finally块就是一段独立的代码. 无论是try块还是catch块, 在离开前都会跳转去执行finally中的代码. 但是此时try和catch中的所有代码都已经执行完毕, 因此在运行finally时是不会对前面的结果造成任何影响的. 如此说来编译器不许我们将return之类的代码放到finally中就是很好理解的事情了. ^_^

注1: 在标准C++中是没有finally的, 只有try和catch. 但是在VC中有宏 _try.._except.._finally 可以实现相同的效果. 呵呵, 感谢江南同志!
posted @ 2007-07-10 15:00  Ivan Zou  阅读(1797)  评论(2编辑  收藏  举报