异常处理(C# 编程指南)

C# 程序员使用 try 块来对可能受异常影响的代码进行分区。 关联的 catch 块用于处理生成的任何异常。 finally 块包含无论 try 块中是否引发异常都会运行的代码,如发布 try 块中分配的资源。 try 块需要一个或多个关联的 catch 块或一个 finally 块,或两者皆之。

下面的示例演示 try-catch 语句、try-finally 语句和 try-catch-finally 语句。

try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
    // Only catch exceptions that you know how to handle.
    // Never catch base class System.Exception without
    // rethrowing it at the end of the catch block.
}
try
{
    // Code to try goes here.
}
finally
{
    // Code to execute after the try block goes here.
}
try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
}
finally
{
    // Code to execute after the try (and possibly catch) blocks
    // goes here.
}

一个不具有 catchfinally 块的 try 块会导致编译器错误。

catch 块

catch 块可以指定要捕获的异常的类型。 该类型规范称为异常筛选器。 异常类型应派生自 Exception。 一般情况下,不要将 Exception 指定为异常筛选器,除非了解如何处理可能在 try 块中引发的所有异常,或者已在 catch 块的末尾处包括了 throw 语句

可将具有不同异常类的多个 catch 块链接在一起。 代码中 catch 块的计算顺序为从上到下,但针对引发的每个异常,仅执行一个 catch 块。 将执行指定所引发的异常的确切类型或基类的第一个 catch 块。 如果没有 catch 块指定匹配的异常类,则将选择不具有类型的 catch 块(如果语句中存在)。 务必首先定位具有最具体的(即,最底层派生的)异常类的 catch 块。

当以下条件为 true 时,捕获异常:

  • 能够很好地理解可能会引发异常的原因,并且可以实现特定的恢复,例如捕获 FileNotFoundException 对象时提示用户输入新文件名。
  • 可以创建和引发一个新的、更具体的异常。
    int GetInt(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    
  • 想要先对异常进行部分处理,然后再将其传递以进行更多处理。 在下面的示例中,catch 块用于在重新引发异常之前将条目添加到错误日志。
    try
    {
        // Try to access a resource.
    }
    catch (UnauthorizedAccessException e)
    {
        // Call a custom error logging procedure.
        LogError(e);
        // Re-throw the error.
        throw;
    }
    

还可以指定异常筛选器,以向 catch 子句添加布尔表达式。 异常筛选器表明仅当条件为 true 时,特定 catch 子句才匹配。 在以下示例中,两个 catch 子句均使用相同的异常类,但是会检查其他条件以创建不同的错误消息:

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch (IndexOutOfRangeException e) when (index < 0) 
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be negative.", e);
    }
    catch (IndexOutOfRangeException e)
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be greater than the array size.", e);
    }
}

始终返回 false 的异常筛选器可用于检查所有异常,但不可用于处理异常。 典型用途是记录异常:

public class ExceptionFilter
{
    public static void Main()
    {
        try
        {
            string? s = null;
            Console.WriteLine(s.Length);
        }
        catch (Exception e) when (LogException(e))
        {
        }
        Console.WriteLine("Exception must have been handled");
    }

    private static bool LogException(Exception e)
    {
        Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}");
        Console.WriteLine($"\tMessage: {e.Message}");
        return false;
    }
}

LogException 方法始终返回 false,使用此异常筛选器的 catch 子句均不匹配。 catch 子句可以是通用的,使用 System.Exception后面的子句可以处理更具体的异常类。

Finally 块

finally 块让你可以清理在 try 块中所执行的操作。 如果存在 finally 块,将在执行 try 块和任何匹配的 catch 块之后,最后执行它。 无论是否会引发异常或找到匹配异常类型的 catch 块,finally 块都将始终运行。

finally 块可用于发布资源(如文件流、数据库连接和图形句柄)而无需等待运行时中的垃圾回收器来完成对象。

在下面的示例中,finally 块用于关闭在 try 块中打开的文件。 请注意,在关闭文件之前,将检查文件句柄的状态。 如果 try 块不能打开文件,则文件句柄仍将具有值 nullfinally 块不会尝试将其关闭。 或者,如果在 try 块中成功打开文件,则 finally 块将关闭打开的文件。

FileStream? file = null;
FileInfo fileinfo = new System.IO.FileInfo("./file.txt");
try
{
    file = fileinfo.OpenWrite();
    file.WriteByte(0xF);
}
finally
{
    // Check for null because OpenWrite might have failed.
    file?.Close();
}

C# 语言规范

有关详细信息,请参阅 C# 语言规范中的异常try 语句。 该语言规范是 C# 语法和用法的权威资料。

另请参阅