异常

异常

  • 异常是程序中的运行时错误,它违反了系统约束或应用程序约束,或出现了在正常操作时未预料的情形。
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#Study1128.Abnormity1128.Abnormity\_01WhatAbnormityProgram.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");
            }
        }
    }
}
原文地址:https://www.cnblogs.com/ChaoJieLiu/p/11953486.html