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