异常
异常
- 异常是程序中的运行时错误,它违反了系统约束或应用程序约束,或出现了在正常操作时未预料的情形。
class Program
{
static void Main(string[] args)
{
int x = 10, y = 0;
x /= y;
Console.ReadKey();
}
}
//引发的异常:“System.DivideByZeroException”(位于_01WhatAbnormity.exe 中)
//“System.DivideByZeroException”类型的未经处理的异常在_01WhatAbnormity.exe 中发生 尝试除以零。
try语言
-
用来指明为避免出现异常而被保护的代码段,并在发生异常时提供代码处理异常。
-
3个部分组成:
- 1.try块包含为避免出现异常而被保护的代码。
- 2.catch子句部分含有一个或多个catch子句。这些事处理异常的代码段,它们也称为是异常处理程序。
- 3.finally块含有在所有情况下都要被执行的代码,无论有没有异常发生。
-
处理异常:把有可能导致异常的代码放在一个try块中,并提供一个简单的catch子句,以处理该异常。当异常发生时,它被捕获并在catch块中处理。
class Program
{
static void Main(string[] args)
{
//int x = 10, y = 0;
//x /= y;
//Console.ReadKey();
int x = 10;
try
{
int y = 0;
x /= y;//抛出一个异常
}
catch
{
//处理异常的代码
Console.WriteLine("Handing all exception - Keep on Running");
}
Console.ReadKey();
//输出:Handing all exception - Keep on Running
}
}
异常类
- 当一个异常发生时:
- 1.创建该类型的异常对象;
- 2.寻找适当的catch子句以处理它。
- 3.所有的异常类都从根本上派生自System.Exception类
- 4.异常对象含有只读属性,带有导致该异常的信息
属性 | 类型 | 描述 |
---|---|---|
Message | string | 这个属性含有解释异常原因的消息。 |
StackTrace | string | 这个属性含有描述异常发生在何处的信息 |
InnerException | Exception | 如果当前异常是由另一个异常引起的,这个属性包含前一个异常的引用 |
HelpLink | string | 这个属性可以被应用程序定义的异常设置,为异常原因信息提供URN或URL |
Source | string | 如果没有被应用程序定义的异常设定,那么这个属性含有异常所在的程序集的名称 |
catch子句
- catch子句处理异常的3种形式,允许不同级别的处理:
//一般catch子句
//1.在catch关键字之后没有参数列表
//2.匹配try块中引起的任何类型的异常
catch
{
Statements
}
//特定catch子句
//1.带有异常类的名称作为单一参数
//2.匹配任何该名称类型的异常
catch(ExceptionType)
{
Statements
}
//带对象的特定catch子句
//1.在异常类名称之后包括一个标识符
//2.该标识符在catch子句块中相当于一个本地变量,被称为异常变量
//3.异常变量引用异常对象,并能被用于访问关于该对象的信息。
catch(ExceptionType ExceptionVariable)
{
Statements
}
- 一般catch子句能接受任何异常,但不能确定引发异常的类型。这只允许对任何可能发生的异常的普通处理和清理。
- 特定catch子句形式把一个异常类的名称作为参数。它匹配该指定类或派生自它的异常类的异常。
- 带对象的特定catch子句提供关于异常的最多信息。它匹配该指定类的异常,或派生自它的异常类的异常。它还给出一个异常实例(称为异常变量),是一个对CLR创建的异常对象的引用。可以在catch子句块内部访问异常变量的属性,以获取关于引起异常的详细信息。
使用特定catch子句的示例
此示例只处理DivideByZeroException类的异常。
namespace _01WhatAbnormity
{
class Program
{
static void Main(string[] args)
{
//int x = 10, y = 0;
//x /= y;
//Console.ReadKey();
int x = 10;
try
{
int y = 0;
x /= y;//抛出一个异常
}
catch (DivideByZeroException e)
{
Console.WriteLine("Message:{0}", e.Message);
Console.WriteLine("Source:{0}", e.Source);
Console.WriteLine("Stack:{0}", e.StackTrace);
//输出:
//Message:尝试除以零。
//Source: _01WhatAbnormity
//Stack: 在 _01WhatAbnormity.Program.Main(String[] args) 位置 F:\C#\C#Study\1128.Abnormity\1128.Abnormity\_01WhatAbnormity\Program.cs:行号 21
}
//catch (DivideByZeroException)
//{
// Console.WriteLine("Handing an exception.");
// //输出:Handing an exception.
//}
catch
{
//处理异常的代码
Console.WriteLine("Handing all exception - Keep on Running");
//输出:Handing all exception - Keep on Running
}
Console.ReadKey();
}
}
}
catch子句段
- catch子句的目的是允许你以一种优雅的方式处理异常。
- 若异常是由前一个异常引起的,则可以通过异常变量的InnerException属性来获得对前一个异常对象的引用。
- catch子句段可以包含多个catch子句
- 当异常发生时,系统按顺序搜索catch子句的列表,第一个匹配该异常对象类型的catch子句被执行。
- catch子句的排序的两个重要规则
- 特定catch子句必须以一种顺序排列,最明确的异常类型第一,直到最普通的类型
- 若有一个一般catch子句,它必须是最后一个,并且在所有特定catch子句之后。
finally块
- 若程序的控制流进入了一个带finally块的try语句,那么finally始终会被执行。
- 若在try块内部没有异常发生,则在try块的结尾,控制流跳过任何catch子句并到finally块。
- 若在try块内部发生了异常,则在catch子句段中无论哪一个适当的catch子句被执行,接着就是finally块的执行。
- 即使try块中有return语句或在catch块中抛出一个异常,finally块也总是会在返回到调用代码之前执行。
为异常寻找处理程序
- 若在try块内发生了异常,系统会查看是否有任何一个catch子句能处理该异常。
- 若找到了适当的catch子句,以下3项中的1项会发生。
- 该catch子句被执行。
- 若有finally块,它被执行。
- 执行在try语句的尾部继续。
- 若异常在一个没有被try语句保护的代码中产生,或者try语句没有匹配的异常处理程序,系统将更进一步寻找匹配的处理代码。为此它会按顺序搜索调用栈,来查看是否存在带匹配的处理程序的封装try块。示例:
namespace _02SearchCallStack
{
class Program
{
static void Main(string[] args)
{
MyClass myClass = new MyClass();
try
{
myClass.A();
}
catch(DivideByZeroException e)
{
Console.WriteLine("catch clause in Main()");
}
finally
{
Console.WriteLine("finally clause in Main()");
}
Console.WriteLine("After try statement in Main.");
Console.WriteLine("---Keep running");
Console.ReadKey();
//输出:
//finally clause in B()
//finally clause in A()
//catch clause in Main()
//finally clause in Main()
//After try statement in Main.
//-- - Keep running
}
}
class MyClass
{
public void A()
{
try
{
B();
}
catch(System.NullReferenceException)
{
Console.WriteLine("catch clause in A()");
}
finally
{
Console.WriteLine("finally clause in A()");
}
}
void B()
{
int x = 10, y = 0;
try
{
x /= y;
}
catch(System.IndexOutOfRangeException)
{
Console.WriteLine("catch clause in B()");
}
finally
{
Console.WriteLine("finally clause in B()");
}
}
}
}
抛出异常
- 使用throw语句使代码显式地引发一个异常。
- throw语法:throw ExceptionObject;
namespace _03ThrowException
{
class Program
{
static void Main(string[] args)
{
string s = null;
MyClass.PrintArg(s);
MyClass.PrintArg("Hi there!");
Console.ReadKey();
//输出:
//Message: 值不能为 null。
//参数名: arg
//Hi there!
}
}
class MyClass
{
public static void PrintArg(string arg)
{
try
{
if(null == arg)
{
ArgumentNullException myEx = new ArgumentNullException("arg");//提供null参数的名称
throw myEx;
}
Console.WriteLine(arg);
}
catch(ArgumentNullException e)
{
Console.WriteLine("Message:{0}", e.Message);
}
}
}
}
不带异常对象的抛出
- 在catch块内部,throw语句还可以不带异常对象使用
- 这种形式重新抛出当前异常,系统继续它的搜索,为该异常寻找另外的处理代码。
- 这种形式只能用在catch语句内部。
- 示例:从第一个catch子句内部重新抛出异常:
namespace _04ThrowWithNoException
{
class Program
{
static void Main(string[] args)
{
string s = null;
MyClass.PrintArg(s);
Console.ReadKey();
//输出:
//Inner Catch:值不能为 null。
//参数名: arg
//Outer Catch: Handing an Exception
}
}
class MyClass
{
public static void PrintArg(string arg)
{
try
{
try
{
if(null == arg)
{
ArgumentNullException myEx = new ArgumentNullException("arg");
throw myEx;
}
Console.WriteLine(arg);
}
catch(ArgumentNullException e)
{
Console.WriteLine("Inner Catch:{0}", e.Message);
throw;//重新抛出异常,没有附加参数
}
}
catch
{
Console.WriteLine("Outer Catch: Handing an Exception");
}
}
}
}