博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第十四章 异常处理
阅读量:4931 次
发布时间:2019-06-11

本文共 9494 字,大约阅读时间需要 31 分钟。

1 错误与异常

  程序中的错误有很多种,最典型的一种是语法错误,另一种是代码中的逻辑错误,代码本身没有语法错误,可能在运行过程中出现。

2 C#中的异常处理结构

  C#语言通过try语句提供的控制结构来检测代码中的异常并作出相应的处理。try语句有4种使用方式。

2.1 try-catch语句

  正常情况下try代码段中语句依次执行,而catch代码段不会被执行;一是出现异常,程序控制权就从try语句转到catch语句,并在catch代码段中进行异常处理。

  catch语句可以捕获指定的异常。可以在catch语句上加一对括号,在括号中指定希望捕获的异常类型。

class ExtractExceptionSample    {        static void Main()        {            try            {                Console.Write("请输入整数x:");                int x = int.Parse(Console.ReadLine());                Console.Write("请输入整数y:");                int y = int.Parse(Console.ReadLine());                int result = 24 / (6 - x) / (x - y) / (y - 2);                Console.WriteLine("24 / (6 - x) / (x - y) / (y -2) = {0}", result);            }            catch (FormatException)            {                Console.WriteLine("输入的格式不正确");            }            catch (DivideByZeroException)            {                Console.WriteLine("分母不能为零");            }            catch (Exception)            {                Console.WriteLine("其它错误");            }         }    }

  同时使用多个catch语句时,如果其中两个cathc语句所捕获的异常类存在继承关系,那么要保证捕获派生类的catch语句在前,而捕获基类的catch语句在后。上例中,Exception是所有异常类的基类。

2.2 try-catch-finally语句

  其中同样可以使用多个catch语句,但finally语句要在所有catch语句的后面,而且只能出现一次。不论程序在执行过程中是否发生异常,finally语句中的代码段总是被执行。

2.3 try-finally语句

  由于没有catch语句,实际不并不能处理异常。如果发生异常,该异常将在执行完finally代码段之后被抛出。

2.4 throw语句

  前面介绍的3种语句是用于防止异常出现时程序中止,而throw语句相反,它主动引发一个异常,如果该异常不被捕获将导致程序中止。throw语句的使用格式是在关键字throw之后跟一个异常类型的对象,当程序执行到throw语句时就引发相应的异常,之后的语句不会被执行。throw语句的主要用途是对发生的异常进行描述。

class ThrowSample    {        static void Main()        {            ConsoleRW c1 = new ConsoleRW();            c1.Validate("2004", 5);        }    }    public class ConsoleRW    {        public string Read(string sPrompt)        {            Console.WriteLine("请输入{0}:", sPrompt);            return Console.ReadLine();        }        public void Write(string sPrompt, string sContent)        {            Console.WriteLine("{0}: {1} ", sPrompt, sContent);        }        public void Validate(string sPwd, int iCount)        {            int i = 0;            while (Read("密码") != sPwd)            {                Console.WriteLine("密码错误!");                i++;                if (i > iCount)                    throw (new Exception("密码错误次数超过限制,您没有系统的访问权限。"));            }            Console.WriteLine("通过验证");        }    }

  如果发生多次输入错误,程序的输出将如下:

请输入密码:20045密码错误!请输入密码:251密码错误!请输入密码:455密码错误!请输入密码:126密码错误!请输入密码:12密码错误!请输入密码:154密码错误!未经处理的异常:  System.Exception: 密码错误次数超过限制,您没有系统的访问权限。   在 P16_7.ConsoleRW.Validate(String sPwd, Int32 iCount) 位置 F:\编程学习\C#2.0\csharp2\P16_7\ConsoleRW.cs:行号 29   在 P16_7.ThrowSample.Main() 位置 F:\编程学习\C#2.0\csharp2\P16_7\ThrowSample.cs:行号 13请按任意键继续. . .

  如果在try-catch语句或try-catch-finally语句的try代码段中使用了throw语句,而throw语句产生的异常又被之后的catch语句捕获,那么就在该catch代码段中进行相应的异常处理。而在其它情况下,throw语句产生的异常都会导致代码中止,这也包括在catch代码段中使用的throw语句。

3 异常的层次结构

3.1 异常传播

  当异常在try代码段中被引发时,程序控制权将在异常处理结构中转移,直到找到一个能够处理该异常的catch语句,否则中止程序,这个过程叫做异常传播。异常传播的步骤为:

  (1)如果当前的异常处理结构中上包含能够处理该异常的catch语句,那么程序控制权就转移给第一个这样的catch语句,异常传播结束。

  (2)如果没有找到能够处理该异常的catch语句,则程序通过当前的异常处理结构(如果存在finally代码段则执行它)。

  (3)如果程序到达更外层的一个异常处理结构,则转到第(1)步;

  (4)如果异常在当前的成员方法中没有得到处理,则当前方法的执行代码被中止;若当前方法是程序所在的进程或线程的主方法,则整个程序结束运行;

  (5)程序控制权转移给调用当前方法的代码,重复第(1)步。

class ExceptionPropagateSample    {        static void Main()        {            try            {                OutterMethod(0);                OutterMethod(1);                OutterMethod(2);            }            catch (Exception)            {                Console.WriteLine("发生一般异常");            }        }        public static void OutterMethod(int x)        {            if (x == 2)                throw new Exception();            try            {                MiddleMethod(x);            }            catch (ArithmeticException)            {                Console.WriteLine("发生算术异常");            }        }        public static void MiddleMethod(int x)        {            if (x == 1)                throw new ArithmeticException();            try            {                InnerMethod(x);            }            catch (DivideByZeroException)            {                Console.WriteLine("发生除法异常");            }        }        public static void InnerMethod(int x)        {            if (x == 0)                throw new DivideByZeroException();        }    }

  输出结果:

发生除法异常发生算术异常发生一般异常请按任意键继续. . .

3.2 Exception类

  Exception类是.NET类库中所有其它异常类的基类,即是对所有异常的一般抽象。其构造函数可以不带参数,也可以指定一个字符类型的参数作为描述异常的信息。还可以指定另一个异常对象作为参数构造Exception对象,这表示作为参数的异常对象引发了正在构造的异常对象。

  (参见Exception类的公用属性)。

  Exception类还提供了一个公有方法GetBaseException,用于返回异常的根源。因为一个异常可能引发另一个异常,使用该方法可以到异常链中第一个异常对象。如果异常链中只有当前异常对象,那么调用该方法得到的总是对象本身。

  在catch语句中,除了可以指明要捕获的异常,还可以声明捕获到的异常对象。通过这个对象的属性或方法,就可以得到关于当前异常对象的详细描述。看下面的程序:

class ExceptionDetailSample    {        static void Main()        {            try            {                OutterMethod(0);            }            catch (Exception exp)            {                Console.WriteLine("程序运行过程中发生异常。");                Console.WriteLine("\n错误信息:\n" + exp.Message);                Console.WriteLine("\n引发对象:\n" + exp.Source);                Console.WriteLine("\n帮助文件:\n" + exp.HelpLink);                Console.WriteLine("\n内部异常:\n" + exp.InnerException);                Console.WriteLine("\n堆栈表示:\n" + exp.StackTrace);                Console.WriteLine("\n引发方法:\n" + exp.TargetSite.Name);                Console.WriteLine("\n基础异常:\n" + exp.GetBaseException());            }        }        public static void OutterMethod(int x)        {            if (x == 1)                throw new Exception("发生一般异常:x不能为1");            InnerMethod(x);        }        public static void InnerMethod(int x)        {            if (x == 0)                throw new ArithmeticException("发生算术异常:x不能为0");        }    }
程序运行过程中发生异常。错误信息:发生算术异常:x不能为0引发对象:P16_9帮助文件:内部异常:堆栈表示:   在 P16_9.ExceptionDetailSample.InnerMethod(Int32 x) 位置 F:\编程学习\C#2.0\csharp2\P16_9\ExceptionDetailSample.cs:行号 39   在 P16_9.ExceptionDetailSample.OutterMethod(Int32 x) 位置 F:\编程学习\C#2.0\csharp2\P16_9\ExceptionDetailSample.cs:行号 33   在 P16_9.ExceptionDetailSample.Main() 位置 F:\编程学习\C#2.0\csharp2\P16_9\ExceptionDetailSample.cs:行号 14引发方法:InnerMethod基础异常:System.ArithmeticException: 发生算术异常:x不能为0   在 P16_9.ExceptionDetailSample.InnerMethod(Int32 x) 位置 F:\编程学习\C#2.0\csharp2\P16_9\ExceptionDetailSample.cs:行号 39   在 P16_9.ExceptionDetailSample.OutterMethod(Int32 x) 位置 F:\编程学习\C#2.0\csharp2\P16_9\ExceptionDetailSample.cs:行号 33   在 P16_9.ExceptionDetailSample.Main() 位置 F:\编程学习\C#2.0\csharp2\P16_9\ExceptionDetailSample.cs:行号 14请按任意键继续. . .

3.3 其它一些常见的异常类

3.3.1 SystemException类和ApplicationException类

  它们是Exception的直接派生类中最常用的两个。SystemException类是System命名空间中所有其它异常类的基类,ApplicationException类则表示应用程序发生非致命性错误所引发的异常。和Exception类相比,这两个类并没有提供新的属性或方法。由公共语言运行时引发的异常,通常使用SystemException;而由应用程序自身引发的异常,则通常使用ApplicationException。这只是.NET框架中处理异常的一个建议性的规则,并没有强制性。

3.3.2 与参数有关的异常类

  ArgumentException类和FormatException类都表示由于传递给方法成员的参数发生错误引发的异常,它们都是SystemException的直接派生类。FormatException类在前面已经出现过,表示参数格式错误;而ArgumentException类则表示参数无效。除了继承的属性之外,ArgumentException类还提供了一个string类型的属性ParamName,表示引发异常的参数名称。

  ArgumentException还有两个常用的派生类,其中ArgumentNullException表示将空值null作为参数传递给了方法,而ArgumentOutOfRangeException则表示传递给方法的参数值超出了可接受的范围。

  下面的程序由用户输入年、月、日来构造一个DateTime对象。如果输入的年月日不是整数,则捕获FormatException异常;如果输入超出可接受的范围,则捕获ArgumentOutOfRangeException异常:

class ArgumentExceptionSample    {        static void Main()        {            try            {                Console.Write("请输入年份:");                int year = int.Parse(Console.ReadLine());                Console.Write("请输入月份:");                int month = int.Parse(Console.ReadLine());                Console.Write("请输入日期:");                int day = int.Parse(Console.ReadLine());                DateTime dt1 = new DateTime(year, month, day);                Console.WriteLine("输入时间为:" + dt1.ToLongDateString());            }            catch (FormatException)            {                Console.WriteLine("输入错误:年月日应当是整数");            }            catch (ArgumentOutOfRangeException)            {                Console.WriteLine("输入错误:不是有效的时间格式");            }        }    }

3.3.3 与成员访问有关的异常类

  MemberAccessException类表示访问类的成员失败所引发的异常。失败的原因可能是没有足够的访问权限,也可能是要访问的成员根本不存在。例如,在调用代表类Delegate的DynamicInvoke方法时,如果无法访问代表所封装的方法,就会引发MemberAccessException异常。

  MemberAccessException类的直接派生类有:

  • FieldAccessException,表示访问字段成员失败所引发的异常;

  • MethodAccessException,表示访问方法成员失败所引发的异常;

  • MissingMemberException,表示访问的成员不存在时所引发的异常。

3.3.4 与数组有关的异常

  当访问的下标超过了数组的长度时,将引发IndexOutOfRangeException异常;如果试图在数组中存储不正确的元素,将引发ArrayTypeMismatchException异常;而如果使用了维数错误的数组,将引发RankException异常。这3个类也都是SystemException的直接派生类。

3.3.5 与内存和磁盘操作有关的异常

  涉及到内存和磁盘操作时,引发异常的原因就复杂了:既可以是软件本身的错误,也可能是系统硬件的问题。

  如果程序的运行得不到足够的内存,将引发OutOfMemoryException异常。如果程序引用了内存中的空对象,将引发NullReferenceException异常,该异常类需要和前面介绍的ArgumentNullException类相区别。例如一个对象为空值null,试图调用该对象的字段或方法成员就会引发NullReferenceException异常;而如果将该对象作为一个参数传递给某个方法,而该方法不支持空对象,那么引发的是ArgumentNullException异常。

  IOException类表示在进行文件输入输出操作时所引发的异常,它的5个直接派生类分别是:

  • DirectoryNotFoundException,表示没有找到指定的目录而引发的异常;

  • FileNotFoundException,表示没有找到指定的文件而引发的异常;

  • EndOfStreamException,表示已经到达流的末尾而引发的异常;

  • FileLoadException,表示不能加载文件而引发的异常;

  • PathToolLongException,表示文件或目录的路径名超出规定的长度而引发的异常。

3.3.6 与算术运算有关的异常

  ArithmeticException类表示与算术运算有达的所有异常类的基类。其派生类有:

  • DivideByZeroException,表示整数或十进制运算中试图除以零时所引发的异常;

  • NotFiniteNumberException,表示浮点数运算中出现正负无穷大或非数值时所引发的异常;

  • OverflowException,表示运算溢出所引发的异常。

转载于:https://www.cnblogs.com/boywg/p/4149970.html

你可能感兴趣的文章
怎样取php一个字符串中的某个字符
查看>>
我的友情链接
查看>>
RedHat6 管理应用服务【11】
查看>>
stm32F10x复习-1
查看>>
redis的学习使用(ubuntu系统下)
查看>>
20135226黄坤信息安全系统设计基础期末总结
查看>>
轻松快捷创建VSFTP虚拟用户
查看>>
[转]Javascript原型继承
查看>>
[转] vue异步处理错误
查看>>
CSS 3D动画概述菜鸟级解读之一
查看>>
分布式系列文章 —— 从 ACID 到 CAP / BASE
查看>>
方法签名与方法重载
查看>>
cmake 变量
查看>>
[Programming Entity Framework] 第2章 探究实体数据模型(EDM)(一)
查看>>
shell环境
查看>>
Java调用C++类库--JNI
查看>>
gles和opengl版本对照表
查看>>
微信开发(二)自己的代码
查看>>
python netwokx环境搭建
查看>>
面向空实现类继承
查看>>