如何在 C# 中的收益返回方法中正确抛出异常
本文关键字:方法 抛出异常 返回 收益 | 更新日期: 2023-09-27 17:55:38
请参阅下面的编辑,以重现我在此问题中描述的行为。
以下程序永远不会结束,因为 C# 中的yield return
构造在引发异常时无限期地调用 GetStrings()
方法。
class Program
{
static void Main(string[] args)
{
// I expect the Exception to be thrown here, but it's not
foreach (var str in GetStrings())
{
Console.WriteLine(str);
}
}
private static IEnumerable<string> GetStrings()
{
// REPEATEDLY throws this exception
throw new Exception();
yield break;
}
}
对于这个微不足道的例子,我显然可以使用return Enumerable.Empty<string>();
代替,问题就消失了。 但是,在一个更有趣的示例中,我希望将异常抛出一次,然后停止调用该方法并在"消耗"IEnumerable
的方法中抛出异常。
有没有办法产生这种行为?
编辑:好的,问题与我最初想象的不同。 上面的程序不会结束,foreach
循环的行为就像一个无限循环。 下面的程序确实结束,异常显示在控制台上。
class Program
{
static void Main(string[] args)
{
try
{
foreach (var str in GetStrings())
{
Console.WriteLine(str);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static IEnumerable<string> GetStrings()
{
throw new Exception();
yield break;
}
}
为什么try
... 在这种情况下,catch
块会有所不同? 这对我来说似乎很奇怪。 感谢@AndrewKilburn的回答已经指出了我这一点。
编辑#2:在命令提示符下,程序在这两种情况下执行相同的操作。 在Visual Studio Enterprise 2015 Update 2中,无论我在调试还是发布中编译,上述行为都是我所看到的。 随着try
... catch
,程序以异常结束,没有它,Visual Studio永远不会关闭程序。
编辑#3:固定对我来说,这个问题通过@MartinBrown的答案解决了。 当我取消选中"调试>选项>"下的"调试选项">常规">"在未经处理的异常上展开调用堆栈"时,此问题消失了。 当我再次选中该框时,问题又回来了。
这里看到的行为不是代码中的错误;而是Visual Studio调试器的副作用。这可以通过在Visual Studio中关闭堆栈展开来解决。尝试进入Visual Studio选项调试/常规并取消选中"在未处理的异常时展开调用堆栈"。然后再次运行代码。
发生的情况是,当您的代码遇到完全未经处理的异常时,Visual Studio 会将调用堆栈展开到代码中导致异常的行之前。这样做是为了您可以编辑代码并继续执行编辑后的代码。
此处看到的问题看起来像一个无限循环,因为当您在调试器中重新启动执行时,要运行的下一行是刚刚导致异常的行。在调试器外部,调用堆栈将在未处理的异常上完全展开,因此不会导致调试器中相同的循环。
此堆栈展开功能可以在设置中关闭,默认情况下处于启用状态。但是,将其关闭将阻止您编辑代码并继续,而无需先自己展开堆栈。但是,无论是从调用堆栈窗口还是简单地从异常助手中选择"启用编辑",这都很容易做到。
以下程序永远不会结束
这是错误的。 该计划将很快结束。
因为 C# 中的收益返回构造在引发异常时无限期地调用
GetStrings()
方法。
这是错误的。 它根本不这样做。
我希望异常被抛出一次,然后停止调用该方法并在"消耗"
IEnumerable
的方法中抛出异常。
这正是确实发生的事情。
有没有办法产生这种行为?
使用您已经提供的代码。
class Program {
static void Main(string[] args) {
try {
foreach (var item in GetStrings()) {
Console.WriteLine();
}
}
catch (Exception ex) {
}
}
private static IEnumerable<string> GetStrings() {
// REPEATEDLY throws this exception
throw new Exception();
yield break;
}
}
把它放在一个尝试捕捉中会导致它爆发并做任何你想做的事情
class Program
{
public static int EnumerableCount;
static void Main(string[] args)
{
EnumerableCount = 0;
try
{
foreach (var str in GetStrings())
{
Console.WriteLine(str);
Console.Read();
}
}
catch (Exception e)
{
Console.WriteLine(e);
Console.Read();
}
}
private static IEnumerable<string> GetStrings()
{
EnumerableCount++;
var errorMessage = string.Format("EnumerableCount: {0}", EnumerableCount);
throw new Exception(errorMessage);
yield break;
}
}
具有以下输出:
System.Exception: EnumerableCount: 1
at {insert stack trace here}
执行流进入 GetStrings() 方法,对于第一次迭代,异常被抛出并在 Main() 方法中捕获。按回车键后,程序退出。
在 Main() 方法中删除 try catch 会导致异常无法处理。然后输出为:
Unhandled Exception: System.Exception: EnumerableCount: 1
at {insert stack trace here}
程序崩溃。