C# 学习笔记 - 1.6 - 异常

发布时间 2023-08-21 20:31:17作者: Snoopy1866

异常

概述

C# 的异常捕获系统允许开发者将正常代码与异常处理逻辑进行分离。

异常可以表示在软件执行期间出现的各种异常情况,包括内部的和外部的。

外部条件导致的异常:网络故障、权限不足、内存不足、Web服务引发的异常,这些异常通常由操作系统、.NET 运行时或外部应用程序引发;

内部条件导致的异常:软件缺陷、设计的功能故障、传播的外部故障,例如运行时检测到空对象引用、用户输入的无效字符串。

用户自定义的异常类应当继承 Exception 类。

C# 有三种捕获异常的定义:

  • try / catch : 捕获并处理
  • try / catch / finally : 捕获并处理,同时不管是否捕获到异常,始终执行 finally 语句块
  • try / finally : 始终执行 finally 语句块,任何发生的异常都将在 finally 语句块后抛出

异常按照从具体到抽象的顺序被捕获,例如:如果尝试访问一个不存在的文件,CLR 将按照以下顺序查找异常处理器(exception handlers):

  • FileNotFoundException
  • IOException(FileNotFoundException 的基类)
  • SystemException(IOException 的基类)
  • Exception(SystemException 的基类)

如果抛出的异常不是派生自要捕获的异常列表,或者不在要捕获的例外列表中,则会将其抛出调用堆栈。

If the exception being thrown does not derive from or is not in the list of exceptions to catch, it is thrown up the call stack.

举例

try / catch

捕获单个异常:

class ExceptionTest
{
     public static void Main(string[] args)
     {
          try
          {
               Console.WriteLine(args[0]);
               Console.WriteLine(args[1]);
               Console.WriteLine(args[2]);
               Console.WriteLine(args[3]);
               Console.WriteLine(args[4]);
          }
          catch (ArgumentOutOfRangeException e)
          {
               Console.WriteLine(e.Message);
          }
     }
}

捕获多个异常:

class ExceptionTest
{
     public static void Main(string[] args)
     {
          try
          {
               string fileContents = new StreamReader(@"C:\log.txt").ReadToEnd();
          }
          catch (UnauthorizedAccessException e) // Access problems
          {
               Console.WriteLine(e.Message);
          }
          catch (FileNotFoundException e)       // File does not exist
          {
               Console.WriteLine(e.Message);
          }
          catch (IOException e)                // Some other IO problem.
          {
               Console.WriteLine(e.Message);
          }
     }
}

catch 语句中,可以省略异常类型和异常变量名,例如:

try
{
    int number = 1/0;
}
catch (DivideByZeroException)
{
    // DivideByZeroException
}
catch
{
    // some other exception
}

try / catch / finally

using System;
class ExceptionTest
{
     public static void Main(string[] args)
     {
          SqlConnection sqlConn = null;

          try
          {
              sqlConn = new SqlConnection ( /*Connection here*/ );
              sqlConn.Open();
 
              // Various DB things
        
              // Notice you do not need to explicitly close the connection, as .Dispose() does this for you.
          }
          catch (SqlException e)
          {
               Console.WriteLine(e.Message);
          }
          finally
          {
               if (sqlConn != null && sqlConn.State != ConnectionState.Closed)
               {
                   sqlConn.Dispose();
               }
          }
     }
}

注意到在以上代码中,SqlConnection 对象在 try/catch/finally 之外定义,这是因为在 try/catch 语句块内定义的变量无法在 finally 语句块中被访问。

try / finally

class ExceptionTest
{
     public static void Main(string[] args)
     {
          SqlConnection sqlConn = null;

          try
          {
              SqlConnection sqlConn = new SqlConnection ( /*Connection here*/ );
              sqlConn.Open();
 
              // Various DB bits
          }
          finally
          {
               if (sqlConn != null && sqlConn.State != ConnectionState.Closed)
               {
                   sqlConn.Dispose();
               }
          }
     }
}

Re-throwing exceptions

Re-throwing exceptions