C# 异常处理
错误的出现并不总是程序员的原因,有时应用程序会因为用户引发或运行代码的环境而发生错误,无论如何我们都应相应的预测应用程序和代码中出现的错误。C sharp中的异常用于处理系统级和应用程序级的错误状态,它是一种结构化.统一的类型安全的处理机制。
在C#中所有的异常类型都继承自System.Exception,也就是说,System.Exception是所有异常类的基类. 总起来说,其派生类分为两种:
1).SystemException类: 所有的CLR提供的异常类型都是由SystemException派生。
2). ApplicationException类: 由用户程序引发,用于派生自定义的异常类型,一般不直接进行实例化。
c#的异常 机制非常类似于c++的异常处理机制,但是还是有一些重要的区别:
1).在 C# 中,所有的异常必须由从 System.Exception 派生的类类型的实例来表示。在 C++ 中,可以使用任何类型的任何值表示异常。
2).在 C# 中,利用 finally 块可编写在正常执行和异常情况下都将执行的终止代码。在 C++ 中,很难在不重复代码的情况下编写这样的代码。
3).C# 中,系统级的异常如溢出.被零除和 null 等都对应地定义了与其匹配的异常类,并且与应用程序级的错误状态处于同等地位。
1.1 导致异常的原因
可以以两种不同的方式引发异常。
1).使用 throw主动抛出异常,用于立即无条件地引发异常。控制永远不会到达紧跟在 throw 后面的语句。
2).在执行 C# 语句和表达式的过程中,有时会出现一些例外情况,使某些操作无法正常完成,此时就会引发一个异常。例如,在整数除法运算中,如果分母为零,则会引发 System.DivideByZeroException。有关可能以此方式引发的各种异常的列表。
1.2 System.Exception 类
System.Exception 类是所有异常的基类型。此类具有一些所有异常共享的值得注意的属性:
1).Message 是 string 类型的一个只读属性,它包含关于所发生异常的原因的描述(易于人工阅读)
2).Source 导致异常的应用程序名或对象名
3).Data 这个属性可以给异常添加键/值语句,以提供关于异常的额外信息
4).StackTrace 栈上方法调用的详细信息,它有助于跟踪抛出异常的方法
5).InnerException 是 Exception 类型的一个只读属性。如果它的值不是 null,则它所引用的是导致了当前异常的那个异常,即表示当前异常是在处理那个 InnerException 的 catch 块中被引发的。否则,它的值为 null,则表示该异常不是由另一个异常引发的。
1.3 异常的处理方式
1).程序在try块中发生异常或遇到一条throw语句,会立刻查找与这个try对应的catch块(根据该异常的运行时类型来确定),如果有多个与try对应的catch块,则会查找与catch块对应的异常类(注意,没有指定异常类的 catch 子句和catch(Exception)一样可以处理任何异常)。
2).程序在try块中发生异常或遇到一条throw语句,系统会立即退出栈上所有的方法调用,异常处后面的代码都不会执行,此时,中间方法调用中的所有局部变量都会超出作用域。
3).无论是否抛出异常,finally块都会执行,可以用于清理资源或执行通常要在try或catch块末尾执行的其他操作(如删除对象或关闭已打开的对象)。
4).try对应多个catch块时,程序只执行它在可用的catch块列表中第一个合适的catch块,所以,最先编写catch块用于处理非常特殊的错误,接着比较一般的块,顺序很重要。
5).嵌套try...catch,外层抛出的异常外层catch捕获处理,内层抛出的异常内层catch捕获处理,如果内层对应没有catch,则外层Catch处理,如外层也没Catch,则.NET运行库处理。
6).在Catch中抛出或发生异常,抛出或发生异常后面的代码不会运行,执行finally块中的代码后跳出当前等级的异常处理,由上层Catch捕获,如果没有,则由.NET运行库捕获。
7).有时抛出一个异常后,代码中没有catch块能够处理这类异常。实际上.NET运行库把整个程序放在另一个更大try块中,对每个.NET程序它都会这么做。它可以捕获任何类型的异常,程序流会退出程序,由.NET运行库中的catch块捕获它。
1.4用户自定义异常
创建自定义异常类应严格遵循几个原则
1). 声明可序列化(用于进行系列化,当然如果你不需要序列化。那么可以不声明为可序列化的)
2). 添加一个默认的构造函数
3). 添加包含message的构造函数
4). 添加一个包含message,及内部异常类型参数的构造函数
5). 添加一个序列化信息相关参数的构造函数.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class MyException : ApplicationException { private string message; public MyException() : base() { } public MyException(string str) :base(str) { } public MyException(string str, Exception inner) : base(str, inner) { } } class Program { static void Main(string[] args) { try { while (true) { try { Console.WriteLine("Input a Number between 0-5"); string userInput; userInput = Console.ReadLine(); if ("" == userInput) { throw new MyException("throw my exception"); break; } int nNum = Convert.ToInt32(userInput); if (nNum < 0 || nNum > 5) { throw new IndexOutOfRangeException("you type in " + userInput); } Console.WriteLine("You Number was " + nNum); } catch (MyException me) { Console.WriteLine("catch a myself exception"); } catch (IndexOutOfRangeException ex) { Console.WriteLine("You should Input a Number between 0-5"); throw new MyException("throw in catch"); } catch (Exception e) { Console.WriteLine("Error Message is " + e.Message); } finally { Console.WriteLine("Thank You"); } } } catch(MyException m) { Console.WriteLine("Hi, l catch a Exception"); Console.WriteLine(m.Message); } Console.ReadLine(); } } }
1.5公共异常类
下列异常由某些 C# 操作引发。
System.ArrayTypeMismatchException
当存储一个数组时,如果由于被存储的元素的实际类型与数组的实际类型不兼容而导致存储失败,就会引发此异常。
System.DivideByZeroException
在试图用零除整数值时引发。
System.IndexOutOfRangeException
在试图使用小于零或超出数组界限的下标索引数组时引发。
System.InvalidCastException
当从基类型或接口到派生类型的显式转换在运行时失败时,就会引发此异常。
System.NullReferenceException
在需要使用引用对象的场合,如果使用 null 引用,就会引发此异常。
System.OutOfMemoryException
在分配内存(通过 new)的尝试失败时引发。
System.OverflowException
在 checked 上下文中的算术运算溢出时引发。
System.StackOverflowException
当执行堆栈由于保存了太多挂起的方法调用而耗尽时,就会引发此异常;这通常表明存在非常深或无限的递归。
System.TypeInitializationException
在静态构造函数引发异常并且没有可以捕捉到它的 catch 子句时引发。