练习 - 创建并引发异常
开发人员通常需要从方法中创建和引发异常,然后在可以处理这些异常的调用堆栈中进一步捕获这些异常。 异常处理有助于确保应用程序的稳定性。
在本练习中,你将从示例应用程序开始,该示例应用程序在调用的方法中包含潜在的错误条件。 更新后的方法在检测到问题时会出现 throw 异常。 异常将在调用方法的代码块中 catch 处理。 结果是一个应用程序,可提供更好的用户体验。
创建新的代码项目
第一步是创建可在本模块中使用的代码项目。
打开“Visual Studio Code”的新实例。
在“文件”菜单中,选择“打开文件夹”。
在 “打开文件夹 ”对话框中,导航到 Windows 桌面 文件夹。
在 “打开文件夹 ”对话框中,选择“ 新建文件夹”。
将新文件夹 命名为 ThrowExceptions101,然后选择“ 选择文件夹”。
在“终端”菜单中,选择“新终端”。
你将使用 .NET CLI 命令创建新的控制台应用。
在终端面板命令提示符处,输入以下命令:
dotnet new console关闭“终端”面板。
查看示例应用程序
使用以下步骤加载和查看示例应用程序。
打开 Program.cs 文件。
在“视图”菜单中,选择“命令面板”。
在命令提示符下,输入 .net:g,然后选择 .NET:生成构建和调试的资产。
将 Program.cs 文件的内容替换为以下代码:
// Prompt the user for the lower and upper bounds Console.Write("Enter the lower bound: "); int lowerBound = int.Parse(Console.ReadLine()); Console.Write("Enter the upper bound: "); int upperBound = int.Parse(Console.ReadLine()); decimal averageValue = 0; // Calculate the sum of the even numbers between the bounds averageValue = AverageOfEvenNumbers(lowerBound, upperBound); // Display the value returned by AverageOfEvenNumbers in the console Console.WriteLine($"The average of even numbers between {lowerBound} and {upperBound} is {averageValue}."); // Wait for user input Console.ReadLine(); static decimal AverageOfEvenNumbers(int lowerBound, int upperBound) { int sum = 0; int count = 0; decimal average = 0; for (int i = lowerBound; i <= upperBound; i++) { if (i % 2 == 0) { sum += i; count++; } } average = (decimal)sum / count; return average; }花点时间查看代码。
请注意,应用程序执行以下任务:
顶级语句使用
Console.ReadLine()语句获取其值lowerBound。upperBound调用方法时
AverageOfEvenNumbers,顶级语句传递lowerBound并upperBound作为参数。AverageOfEvenNumbers方法执行以下任务:- 声明计算中使用的局部变量。
-
for使用循环对介于和之间的lowerBound偶数求和upperBound。 总和存储在sum. - 计算总和中包含的数字数。 计数存储在
count. - 将求和数字的平均值存储在名为 <
a0/a0> 的变量中。 返回的值 average。
顶级语句将输出由控制台返回
AverageOfEvenNumbers的值,然后暂停执行。
配置调试环境
示例应用程序从控制台读取用户输入。 调试控制台面板不支持从控制台读取输入。 需要先更新 launch.json 文件,然后才能在调试器中运行此应用程序。
使用“资源管理器”视图打开 launch.json 文件。
在 launch.json 文件中,更新
console属性,如下所示:// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console "console":"integratedTerminal",console属性的默认值为internalConsole,与“调试控制台”面板一致。 遗憾的是,“调试控制台”面板不支持控制台输入。integratedTerminal设置支持控制台输入和输出,与“终端”面板一致。将更改保存到 launch.json 文件,然后关闭该文件。
在 Visual Studio Code 的“运行”菜单上,选择“开始调试”。
切换到终端面板。
在“下限”提示符下,输入 3
在“上限”提示符下,输入 11
请注意,应用程序会显示以下消息,然后暂停:
The average of even numbers between 3 and 11 is 7.若要退出应用程序,请按 Enter。
在 AverageOfEvenNumbers 方法中引发异常
该方法 AverageOfEvenNumbers 需要大于下限的上限。
DivideByZero如果下限大于或等于上限,则会发生错误。
当下限大于或等于上限时,需要更新 AverageOfEvenNumbers 方法以引发异常。
花点时间考虑如何解决问题。
一个选项是包装代码块内的
try计算average,并在catchDivideByZero发生异常时进行。 可以重新引发异常,然后在调用代码中处理该异常。另一个选项是在开始计算之前评估输入参数。 如果
lowerBound大于或等于upperBound,可以引发异常。在开始计算之前评估参数并引发异常是更好的选择。
考虑要引发的异常类型。
有两种与问题相符的异常类型:
-
ArgumentOutOfRangeExceptionArgumentOutOfRangeException- 仅当参数的值超出被调用方法定义的允许值范围之外时,才应引发异常类型。 虽然AverageOfEvenNumbers没有显式定义允许的范围lowerBound,或者upperBound,其lowerBound值确实意味着允许的范围upperBound。 -
InvalidOperationExceptionInvalidOperationException:仅当方法的作条件不支持成功完成特定方法调用时,才应引发异常类型。 在这种情况下,作条件由方法的输入参数建立。
如果有两种或更多个要从中选择的异常类型,请选择更贴近问题的异常类型。 在这种情况下,这两种异常类型均与问题保持一致。
如果两个或多个与问题保持一致的异常类型,请选择范围最窄的异常类型。
ArgumentOutOfRangeException异常类型的范围限定为传递给方法的参数。 异常InvalidOperationException类型的范围限定为方法的作条件。 在这种情况下,ArgumentOutOfRangeException异常类型的范围比InvalidOperationException异常类型更窄。该方法
AverageOfEvenNumbers应引发异常ArgumentOutOfRangeException。-
在方法顶部
AverageOfEvenNumbers,若要检测上限问题,请按如下所示更新代码:if (lowerBound >= upperBound) { } int sum = 0;若要创建并引发
ArgumentOutOfRangeException异常,请按如下所示更新if代码块:if (lowerBound >= upperBound) { throw new ArgumentOutOfRangeException("upperBound", "ArgumentOutOfRangeException: upper bound must be greater than lower bound."); }此代码行使用导致异常和指定的错误消息的输入参数的名称初始化类的新实例
ArgumentOutOfRangeException。
捕获调用代码中的异常
如果可能,应在可以处理异常的调用堆栈级别捕获异常。 在此示例应用程序中,方法的参数 AverageOfEvenNumbers 可以在调用方法(顶级语句)中管理。
向上滚动到顶级语句。
若要将方法调用和
Console.WriteLine语句括在AverageOfEvenNumbers代码块内try,请按如下所示更新代码:try { // Calculate the sum of the even numbers between the bounds averageValue = AverageOfEvenNumbers(lowerBound, upperBound); // Display the result to the user Console.WriteLine($"The average of even numbers between {lowerBound} and {upperBound} is {averageValue}."); }若要创建关联的
catch子句,请输入以下代码:catch(ArgumentOutOfRangeException ex) { }花点时间考虑如何处理异常。
若要处理此异常,代码需要执行以下作:
- 向用户解释问题。
- 获取新值
upperBound。 - 使用新
upperBound调用AverageOfEvenNumbers。 -
catch如果提供的新upperBound项仍然小于或等于lowerBound,请继续执行异常。
继续执行
catch异常需要循环。 由于至少要调用AverageOfEvenNumbers一次方法,因此应使用循环do。若要将和
catch块括try在循环do中,请按如下所示更新代码:do { try { // Calculate the sum of the even numbers between the bounds averageValue = AverageOfEvenNumbers(lowerBound, upperBound); // Display the result to the user Console.WriteLine($"The average of even numbers between {lowerBound} and {upperBound} is {averageValue}."); } catch (ArgumentOutOfRangeException ex) { } }while定义循环的do退出条件需要表达式。 在定义代码块的内容do之前,很难指定条件。 完成catch代码块将帮助你定义while所需的表达式。若要向用户解释问题并获取新的
upperBound代码块,请按如下所示更新catch代码块:catch (ArgumentOutOfRangeException ex) { Console.WriteLine("An error has occurred."); Console.WriteLine(ex.Message); Console.WriteLine($"The upper bound must be greater than {lowerBound}"); Console.Write($"Enter a new upper bound: "); upperBound = int.Parse(Console.ReadLine()); }更新
catch的代码块描述问题,并要求用户输入新的上限。 但是,如果用户没有输入有效的上限值,该怎么办? 如果用户需要退出循环而不是输入值,该怎么办?若要为用户提供退出循环的选项,而不是输入新的上限,请按如下所示更新
catch代码块:catch (ArgumentOutOfRangeException ex) { Console.WriteLine("An error has occurred."); Console.WriteLine(ex.Message); Console.WriteLine($"The upper bound must be greater than {lowerBound}"); Console.Write($"Enter a new upper bound (or enter Exit to quit): "); string? userResponse = Console.ReadLine(); if (userResponse.ToLower().Contains("exit")) { } else { upperBound = int.Parse(userResponse); } }更新
catch的代码块包括两个路径:“exit”路径和“新的上限”路径。花一分钟时间考虑
while循环所需的do表达式。如果用户在提示符下输入“退出”,代码应退出循环。 如果用户输入新的上限,循环应继续。
while可以使用计算布尔值的表达式。 例如:while (exit == false);建议的
while表达式将建立以下行为:- 只要布尔
exit值等于false,循环do就会继续循环访问。 - 一旦布尔
exit值等于true,循环do就会停止迭代。
- 只要布尔
若要实例化一个名为
exit布尔变量,并用于exit设置循环的do退出条件,请按如下所示更新代码:bool exit = false; do { try { // Calculate the sum of the even numbers between the bounds averageValue = AverageOfEvenNumbers(lowerBound, upperBound); // Display the result to the user Console.WriteLine($"The average of even numbers between {lowerBound} and {upperBound} is {averageValue}."); exit = true; } catch (ArgumentOutOfRangeException ex) { Console.WriteLine("An error has occurred."); Console.WriteLine(ex.Message); Console.WriteLine($"The upper bound must be greater than {lowerBound}"); Console.Write($"Enter a new upper bound (or enter Exit to quit): "); string? userResponse = Console.ReadLine(); if (userResponse.ToLower().Contains("exit")) { exit = true; } else { exit = false; upperBound = int.Parse(userResponse); } } } while (exit == false);保存更新的代码。
在“运行”菜单上,选择“开始调试”。
切换到终端面板。
在“下限”提示符下,输入 3
在“上限”提示符下,输入 3
请注意,终端面板中显示以下输出:
Enter the lower bound: 3 Enter the upper bound: 3 An error has occurred. ArgumentOutOfRangeException: upper bound must be greater than lower bound. (Parameter 'upperBound') The upper bound must be greater than 3 Enter a new upper bound (or enter Exit to quit):在提示输入新的上限时,输入 11
请注意,终端面板中显示以下输出:
Enter the lower bound: 3 Enter the upper bound: 3 An error has occurred. ArgumentOutOfRangeException: upper bound must be greater than lower bound. (Parameter 'upperBound') The upper bound must be greater than 3 Enter a new upper bound (or enter Exit to quit): 11 The average of even numbers between 3 and 11 is 7.若要退出应用程序,请按 Enter。
祝贺! 已成功引发、捕获和处理异常。
回顾
在本单元中,应谨记以下几个重要事项:
- 确保调试环境配置为支持应用程序要求。
- 检测到问题或条件时,方法代码应引发异常。
- 异常应在可解析的调用堆栈中的级别捕获。