如何在C#

时间:2016-07-07 15:46:56

标签: c# exception yield-return

请参阅下面的编辑,以重现我在此问题中描述的行为。

以下程序永远不会结束,因为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>();代替,问题就消失了。然而,在一个更有趣的例子中,我希望抛出异常一次,然后让方法停止被调用并在方法中抛出异常&#34;消费&#34; 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:编辑#2: 从命令提示符,程序在两种情况下都执行相同的操作。在Visual Studio Enterprise 2015 Update 2中,无论是在Debug还是Release中编译,上面的行为就是我所看到的。使用try ... catch,程序以异常结束,没有它,Visual Studio永远不会关闭程序。

编辑#3:已修复 对我来说,问题是由@MartinBrown的答案解决的。当我取消选中Debug&gt;下的Visual Studio选项时选项&gt;调试&gt;一般&gt; &#34;在未处理的例外情况下展开调用堆栈&#34;这个问题消失了。当我再次勾选此框时,问题又会出现。

4 个答案:

答案 0 :(得分:6)

这里看到的行为不是代码中的错误;相反,它是Visual Studio调试器的副作用。这可以通过在Visual Studio中关闭堆栈展开来解决。尝试进入Visual Studio选项Debugging / General并取消选中&#34;在未处理的异常&#34;上展开调用堆栈。然后再次运行代码。

当您的代码遇到完全未处理的异常时,Visual Studio会将调用堆栈展开到导致异常的代码行之前。这样做是为了您可以编辑代码并继续使用编辑过的代码执行。

这里看到的问题看起来像一个无限循环,因为当您在调试器中重新开始执行时,下一行要运行的是刚刚引发异常的行。在调试器之外,调用堆栈将在未处理的异常上完全展开,因此不会导致调试器中出现相同的循环。

此堆栈展开功能可以在设置中关闭,默认情况下已启用。然而,将其关闭将阻止您编辑代码并继续,而无需先自行展开堆栈。但是,通过调用堆栈窗口或只需选择“启用编辑”即可轻松完成此操作。来自例外助理。

答案 1 :(得分:1)

  

以下程序永远不会结束

那是假的。该计划将很快结束。

  

因为C#中的yield return构造在抛出异常时无限期地调用GetStrings()方法。

这是错误的。它根本不会这样做。

  

我希望抛出异常一次,然后停止调用方法并在“消耗”IEnumerable的方法中抛出异常。

这正是发生的事情。

  

有没有办法产生这种行为?

使用您已提供的代码。

答案 2 :(得分:0)

  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;
    }
}

将它放入试试捕获会导致它爆发并做任何你想做的事情

答案 3 :(得分:0)

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}

并且程序崩溃了。