一、为什么需要处理异常
二、异常的种类
1.语法异常
2.逻辑异常。比如10-2=22;
3.编译异常。比如除数为0;
三、如何处理异常
try{} catch{} finnally{};
四、几个案例
案例一、
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 M1(); 6 7 Console.ReadKey(); 8 } 9 10 static void M1() 11 { 12 try 13 { 14 Console.WriteLine("==========="); 15 //如果M2的catch中没有throw,把异常往外抛,M1根本不知道M2中出现了异常,这不合适。 16 M2(); 17 18 } 19 catch (Exception ex) 20 { 21 22 Console.WriteLine(ex.Message.ToString()); 23 } 24 finally 25 { 26 Console.WriteLine("这是M1中的finally"); 27 } 28 29 Console.WriteLine("============"); 30 } 31 32 private static void M2() 33 { 34 try 35 { 36 int m = 10; 37 int r = m / 0; 38 } 39 catch (Exception ex) 40 { 41 Console.WriteLine(ex.Message.ToString()); 42 //throw;//表示往外抛出异常,只有在catch中能这么写。 43 } 44 finally 45 { 46 Console.WriteLine("M2中的finally代码"); 47 } 48 49 } 50 }
throw放到catch中表示把异常往外抛出。只能放在catch中,放在其他地方会报错。
案例二、
1 static void Main(string[] args) 2 { 3 T1(); 4 5 Console.ReadKey(); 6 } 7 8 static void T1() 9 { 10 try 11 { 12 Console.WriteLine("11111111111"); 13 14 Console.WriteLine("^^^^^^^^^^^^^^^^^^^^^"); 15 return; 16 Console.WriteLine("222222222222"); 17 } 18 catch (Exception ex) 19 { 20 Console.WriteLine("33333333333"); 21 } 22 finally 23 { 24 Console.WriteLine("444444444444444"); 25 } 26 27 }
反编译后的IL代码:
1 .method private hidebysig static void T1() cil managed 2 { 3 .maxstack 1 4 .locals init ( 5 [0] class [mscorlib]System.Exception exception) 6 L_0000: nop 7 L_0001: nop 8 L_0002: ldstr "11111111111" 9 L_0007: call void [mscorlib]System.Console::WriteLine(string) 10 L_000c: nop 11 L_000d: ldstr "^^^^^^^^^^^^^^^^^^^^^" 12 L_0012: call void [mscorlib]System.Console::WriteLine(string) 13 L_0017: nop 14 L_0018: leave.s L_003a 15 L_001a: stloc.0 16 L_001b: nop 17 L_001c: ldstr "33333333333" 18 L_0021: call void [mscorlib]System.Console::WriteLine(string) 19 L_0026: nop 20 L_0027: nop 21 L_0028: leave.s L_002a 22 L_002a: leave.s L_003a 23 L_002c: nop 24 L_002d: ldstr "444444444444444" 25 L_0032: call void [mscorlib]System.Console::WriteLine(string) 26 L_0037: nop 27 L_0038: nop 28 L_0039: endfinally 29 L_003a: ret 30 .try L_0001 to L_001a catch [mscorlib]System.Exception handler L_001a to L_002a 31 .try L_0001 to L_002c finally handler L_002c to L_003a 32 } 33 34
可见,finally中的代码会执行,并且是在return之前执行。实际上,反编译后的代码return是在最后,我们看到的是编译器处理后的代码。
案例二、
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Console.WriteLine(T1()); 6 7 Console.ReadKey(); 8 } 9 10 static int T1() 11 { 12 try 13 { 14 Console.WriteLine("11111111111"); 15 int m = 10, n = 0; 16 int r = m / n; 17 Console.WriteLine("^^^^^^^^^^^^^^^^^^^^^"); 18 return 100; 19 20 } 21 catch (Exception ex) 22 { 23 Console.WriteLine("33333333333"); 24 return 1000; 25 } 26 finally 27 { 28 Console.WriteLine("444444444444444"); 29 } 30 31 }
为何最后return1000?反编译后我们发现,程序在最开始声明了一个变量,return 100的时候,给变量赋值为100;return 1000的时候,又给变量赋值为1000;最后return这个变量。最后输出的结果为:111111
333333
4444444
1000
反编译后的代码:
1 private static int T1() 2 { 3 int num4; 4 try 5 { 6 Console.WriteLine("11111111111"); 7 int num = 10; 8 int num2 = 0; 9 int num3 = num / num2; 10 Console.WriteLine("^^^^^^^^^^^^^^^^^^^^^"); 11 num4 = 100; 12 } 13 catch (Exception) 14 { 15 Console.WriteLine("33333333333"); 16 num4 = 0x3e8; 17 } 18 finally 19 { 20 Console.WriteLine("444444444444444"); 21 } 22 return num4; 23 } 24 25
案例三、
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Console.WriteLine(T1()); 6 7 Console.ReadKey(); 8 } 9 10 static int T1() 11 { 12 int result = 100; 13 try 14 { 15 result++; 16 return result; 17 18 } 19 catch (Exception ex) 20 { 21 result++; 22 return result; 23 } 24 finally 25 { 26 result++; 27 } 28 29 }
结果为101;因为只要遇到return,不管这个变量是否与前面的变量名是否一样,都会声明一个新的变量,反编译后的代码是:新变量=result(不管怎样,会声明变量,如果有异常,赋值语句不会执行),在finally之后返回这个变量。
反编译后的代码:
1 private static int T1() 2 { 3 int num2; 4 int num = 100; 5 try 6 { 7 num++; 8 num2 = num; 9 } 10 catch (Exception) 11 { 12 num++; 13 num2 = num; 14 } 15 finally 16 { 17 num++; 18 } 19 return num2; 20 }
案例四
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Console.WriteLine(T1()); 6 7 Console.ReadKey(); 8 } 9 10 static int T1() 11 { 12 int result = 100; 13 try 14 { 15 result++; 16 int x = 11, y = 0; 17 Console.WriteLine(x/y); 18 return result; 19 20 } 21 catch (Exception ex) 22 { 23 result++; 24 return result; 25 } 26 finally 27 { 28 result++; 29 } 30 31 }
始终记住,try{}catch{}中的 return 变量/具体的值,等价于 新的变量=值,反编译后的代码是:新变量=变量(不管怎样,会声明新变量,如果有异常,只是赋值语句不会执行),并且在finally之前执行。返回的是新变量,所以结果是102;
案例五
1 class Person 2 { 3 public int Age { get; set; } 4 } 5 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 Console.WriteLine(T1()); 11 12 Console.ReadKey(); 13 } 14 static Person GetPerson() 15 { 16 Person p = new Person(); 17 p.Age = 100; 18 try 19 { 20 p.Age++; 21 //int x=1,y=0; 22 //Console.WriteLine(x/y); 23 return p; 24 } 25 catch (Exception) 26 { 27 p.Age++; 28 return p; 29 } 30 finally 31 { 32 p.Age++; 33 } 34 }
return p的时候,新变量=p;引用类型的值传递,相当于新变量和p同时指向原来的空间。try中的Age++后,赋值给新变量,然后执行finally中的Age++;最后return新变量,由于新变量和p指向同一块内存,所以Age最终改变了,输出102;
如果抛出异常,同样的道理,输出的结果是103;