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