[英]How to properly throw an Exception inside yield return method in C#
请参阅下面的编辑,以重现我在此问题中描述的行为。
以下程序永远不会结束,因为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中,无论是在Debug还是Release中编译,上面的行为就是我所看到的。 使用try
... catch
,程序以异常结束,没有它,Visual Studio永远不会关闭程序。
编辑#3:固定对我来说,问题是由@MartinBrown的答案解决的。 当我在“调试”>“选项”>“调试”>“常规”>“解除对未处理的异常的调用堆栈”下取消选中Visual Studio的选项时,此问题就会消失。 当我再次检查该框时,问题又回来了。
这里看到的行为不是代码中的错误; 相反,它是Visual Studio调试器的副作用。 这可以通过在Visual Studio中关闭堆栈展开来解决。 尝试进入Visual Studio选项Debugging / General并取消选中“在未处理的异常上展开调用堆栈”。 然后再次运行代码。
当您的代码遇到完全未处理的异常时,Visual Studio会将调用堆栈展开到代码中导致异常的行之前。 它这样做是为了您可以编辑代码并继续执行编辑的代码。
这里看到的问题看起来像一个无限循环,因为当您在调试器中重新开始执行时,下一行要运行的是刚引起异常的那一行。 在调试器之外,调用堆栈将在未处理的异常上完全展开,因此不会导致您在调试器中获得相同的循环。
可以在设置中关闭此堆栈展开功能,默认情况下启用它。 然而,将其关闭将阻止您编辑代码并继续,而无需先自行展开堆栈。 但是,从调用堆栈窗口或从Exception Assistant中选择“启用编辑”,这很容易。
以下程序永远不会结束
那是假的。 该计划将很快结束。
因为C#中的yield return构造在抛出异常时无限期地调用
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}
程序崩溃了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.