练习 - 实现 try-catch 异常处理

已完成

try-catch 模式包含一个 try 块,后跟一个或多个 catch 子句。 每个 catch 子句指定不同异常类型的处理程序。

引发异常时,公共语言运行时 (CLR) 搜索可以处理异常的 catch 子句。 如果当前正在执行的方法不包含可以处理引发的异常类型的 catch 子句,则 CLR 将搜索调用当前方法的方法。 继续向下搜索调用堆栈,直到找到合适的 catch 子句。 如果未找到任何可以处理异常的 catch 子句,则 CLR 向用户显示一条未处理的异常消息,并停止执行程序。

在本练习中,你将实现基本 try-catch 模式。

创建新代码项目

第一步是创建在本模块期间使用的代码项目。

  1. 打开“Visual Studio Code”的新实例。

  2. 在“文件”菜单中,选择“打开文件夹”

  3. 在“打开文件夹”对话框中,导航到 Windows“桌面”文件夹。

  4. 在“打开文件夹”对话框中,选择“新建文件夹”。

  5. 将新文件夹命名为 Exceptions101,然后选择“选择文件夹”。

  6. 在“终端”菜单中,选择“新终端”。

    你将使用 .NET CLI 命令创建新的控制台应用。

  7. 在“终端”面板命令提示符下,输入以下命令:

    dotnet new console
    
  8. 关闭“终端”面板。

实现简单的 try-catch

  1. 使用 Visual Studio Code EXPLORER 视图打开 Program.cs 文件。

  2. 在“视图”菜单中,选择“命令面板”

  3. 在命令提示符下,输入“.net: g”,然后选择“.NET: 生成用于生成和调试的资产”。

  4. 将 Program.cs 文件的内容替换为以下代码:

    double float1 = 3000.0;
    double float2 = 0.0;
    int number1 = 3000;
    int number2 = 0;
    
    Console.WriteLine(float1 / float2);
    Console.WriteLine(number1 / number2);
    Console.WriteLine("Exit program");
    
  5. 花点时间检查代码。

    请注意,应用程序使用两种数值变量类型:doubleint。 代码使用两种数值类型执行除法计算。

    当精确小数值很重要时,开发人员使用 double 类型变量进行计算。

  6. 在“运行”菜单上,选择“开始调试”。

    请注意,将整数值相除时会引发 DivideByZeroException 异常。

    注意

    你可能已注意到,使用 double 类型变量的方程式能够在不产生错误的情况下完成。 使用 double 类型变量的除零计算返回等于无穷大、负无穷大或“非数值”的结果。 这并非意味着应始终使用 double 类型变量而不是 intdecimal 类型。 正确方法是使用适当类型的变量并实现异常处理来捕获可能发生的任何错误。

  7. 在“调试”工具栏上,选择“继续”。

    Screenshot showing the Continue button on the Debug toolbar.

  8. 花点时间检查应用程序的消息输出。

    ∞
    Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.
       at Program.<Main>$(String[] args) in C:\Users\msuser\Desktop\Exceptions101\Program.cs:line 7
    

    请注意,未经处理的异常导致应用程序在第一个 Console.WriteLine() 语句完成后关闭。

    注意

    默认情况下,Visual Studio Code 使用不同的颜色文本来显示调试程序生成的消息。 这有助于开发人员区分应用程序输出和调试程序消息。 如果想要更简洁地查看应用程序输出,可以将 launch.json 文件配置为使用不同的控制台。 例如,将 console 设置为 integratedTerminal 以使用“终端”面板显示应用程序输出。 调试程序消息始终显示在“调试控制台”面板中。

  9. 将两个计算包含在 try 语句的代码块中,如下所示:

    double float1 = 3000.0;
    double float2 = 0.0;
    int number1 = 3000;
    int number2 = 0;
    
    try
    {
        Console.WriteLine(float1 / float2);
        Console.WriteLine(number1 / number2);
    }
    
    Console.WriteLine("Exit program");
    
  10. 请注意 try 块右括号下的红色波浪线。

    使用 try 语句时,C# 语法需要 catchfinally 子句。

  11. try 代码块下方构造一个 catch 代码块,如下所示:

    try
    {
        Console.WriteLine(float1 / float2);
        Console.WriteLine(number1 / number2);
    }
    catch
    {
        Console.WriteLine("An exception has been caught");
    }
    
  12. 在 Visual Studio Code 的“文件”菜单上,选择“保存”。

  13. 在“运行”菜单上,选择“开始调试”。

  14. 花点时间检查应用程序生成的输出。

    ∞
    An exception has been caught
    Exit program
    
  15. 请注意,尽管异常仍然存在,但应用程序现在可以在关闭之前完成剩余代码行的执行。

    异常处理使你能够在发生异常时控制代码执行。 异常处理有助于确保代码稳定并生成预期结果。

捕获调用方法中引发的异常

在许多情况下,异常是在调用堆栈的某个级别捕获的,该级别低于引发异常的级别。

当引发异常且当前方法未捕获异常时,公共语言运行时将展开堆栈,查找包含可以处理异常的 catch 子句的方法。 将执行找到的第一个可以处理异常的 catch 子句。 如果在调用堆栈中的任何位置找不到适当的 catch 子句,公共语言运行时将终止进程并向用户显示错误消息。

  1. 将 Program.cs 文件中的代码替换为以下代码:

    try
    {
        Process1();
    }
    catch
    {
        Console.WriteLine("An exception has occurred");
    }
    
    Console.WriteLine("Exit program");
    
    static void Process1()
    {
        WriteMessage();
    }
    
    static void WriteMessage()
    {
        double float1 = 3000.0;
        double float2 = 0.0;
        int number1 = 3000;
        int number2 = 0;
    
        Console.WriteLine(float1 / float2);
        Console.WriteLine(number1 / number2);
    }
    
  2. 花点时间查看更新的代码。

    • 顶级语句包括调用 Process1() 方法的 try 代码块。
    • Process1() 方法调用 WriteMessage() 方法。
    • WriteMessage() 方法包含将引发 DivideByZeroException 异常的代码。

    请注意,异常将在位于 trycatch 代码块上方的两个调用堆栈级别的方法中生成。

    Screenshot showing the Call Stack levels between where an exception is thrown and where it is caught.

    顶级语句表示为调用堆栈中名为 Main 的方法。

  3. 在 Visual Studio Code 的“文件”菜单上,选择“保存”。

  4. 在“运行”菜单上,选择“开始调试”。

  5. 花点时间检查应用程序生成的输出。

    ∞
    An exception has occurred
    Exit program
    
  6. 请注意,即使在调用堆栈的两个级别引发异常,仍会成功处理异常。

回顾

在本单元中,应谨记以下几个重要事项:

  • 为应用程序中的 try 指定代码行和 try 代码块范围内发生的 catch 异常实现 try-catch 模式。
  • 使用 catch 子句捕获在同一调用堆栈级别引发的异常。
  • 使用 catch 子句捕获在更高的调用堆栈级别引发的异常。