簡體   English   中英

如何在C#中的yield return方法中正確拋出Exception

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM