翻譯|使用教程|編輯:王香|2018-10-12 10:27:49.000|閱讀 307 次
概述:本文用實際案例講解了yield return運算符的工作原理。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關鏈接:
【下載FastReport.Net最新版本】
yield return運算符是使用C#的程序員中最不為人知的運算符之一。甚至那些了解它的人也不能完全確定他們正確理解其工作原理。必須糾正這個惱人的差距。而且,我希望這篇文章可以幫助你。
yield return運算符返回迭代器中的集合項,并將當前位置移動到下一個元素。yield return運算符的存在將該方法轉換為迭代器。每次迭代器遇到yield return時,它都會返回一個值。此運算符向我們和編譯器發出信號,表示此表達式是迭代器。迭代器的任務是在集合的元素之間移動并返回當前元素的值。許多人習慣于在循環中調用計數器作為迭代器,但事實并非如此,因為計數器不返回值。迭代器由編譯器轉換為“有限狀態機”,跟蹤當前位置并知道如何“移動”到下一個位置。在這種情況下,序列元素的值在訪問它時計算。
這是迭代器的最簡單示例:
public static IEnumerable<int> GetItems() { foreach (var i in List) { yield return i; } }
迭代器只能返回IEnumerable <>類型。
迭代器是更復雜的枚舉器模式的語法快捷方式。當C#編譯器遇到迭代器時,它會將其內容擴展為實現枚舉器模式的CIL代碼。這種封裝大大節省了程序員的時間。迭代器允許您執行所謂的“延遲計算”。這意味著僅在請求時才評估元素的值。為了更好地理解收益率回報如何運作,我們將其與傳統周期進行比較 通過示例,一切都變得清晰。
(1)請注意,使用yield return,我們不需要創建額外的列表來填充值。因此我們節省了內存,因為我們只需要內存用于集合的當前元素。元素處理不分配內存,只分配緩存。
static IEnumerable<int> GetSequence() { Random rand = new Random(); List<int> list = new List<int>(); for (int i = 0; i < 3; i++) list.Add(rand.Next()); return list; } static IEnumerable<int> GetSequence() { Random rand = new Random(); for (int i = 0; i < 3; i++) yield return rand.Next(); }
(2)不計算整個枚舉結果的能力。在這個例子中,我們無限地生成數字:
IEnumerable<int> GetInfinityWithIterator() { var i = 0; while (true) yield return ++i; } IEnumerable<int> GetInfinityWithLoop() { var i = 0; var list = new List<int>(); while (true) list.Add(++i); return list; }
下面來看看他們之間的差異:
foreach(var item in GetInfinityWithIterator().Take(5)) { Console.WriteLine(item); }
我們使用LINQ運算符Take來限制樣本量。在收益率返回的情況下,循環在第五個元素處停止。
foreach(var item in GetInfinityWithLoop().Take(5)) { Console.WriteLine(item); }
你不能打斷列表填寫。結果,我們得到錯誤的內存不足。
(3)執行迭代器后調整集合值的能力。 由于yield在實際處理時返回一個集合元素(例如,當在控制臺中顯示元素的值時),即使在執行迭代器之后,我們也可以更改集合的元素。調用它時,迭代器實際上不會返回實際值。迭代器知道從哪里獲取值。只有當他們真的需要時,他才會歸還他們。這就是所謂的懶惰負載。
IEnumerable<int> MultipleYieldReturn(IEnumerable<int> mass) { foreach (var item in mass) yield return item * item; } IEnumerable<int> MultipleLoop(IEnumerable<int> mass) { var list = new List<int>(); foreach (var item in mass) list.Add(item * item); return list; }
將調用這些方法:
var mass = new List<int>() { 1, 2, 3 }; var MultipleYieldReturn = Helper.MultipleYieldReturn(mass); var MultipleLoop = Helper.MultipleLoop(mass); mass.Add(4); Console.WriteLine(string.Join(",",MultipleYieldReturn)); Console.WriteLine(string.Join(",", MultipleLoop));
結果是:
初始化MultipleYieldReturn和MultipleLoop變量后,我們再向集合中添加一個元素:mass.Add(4);
Console.WriteLine(string.Join(",",MultipleYieldReturn)); Console.WriteLine(string.Join(",", MultipleLoop));
結果:
在將結果輸出到控制臺時,集合包含值4.由于yield return在查詢時生成值,因此迭代器處理了所有當前值。在初始化MultipleLoop變量時運行傳統循環,此時集合僅包含3個值。
(4)具有收益率回報的異常處理具有細微差別。yield return語句不能在try-catch部分中使用,只能在try-finally中使用。例如,如何在不知道約束的情況下編寫:
public IEnumerable TransformData(List<string> data) { foreach (string item in data) { try { yield return PrepareDataRow(item); } catch (Exception ex) { Console.Error.WriteLine(ex.Message); } } }
在這種情況下,catch塊永遠不會捕獲錯誤。這都是延遲執行收益率回報的原因。我們只在使用迭代器的數據進行實際工作時才了解錯誤。例如,當我們將數據從迭代器輸出到控制臺時。在此之前,迭代器不適用于實際數據。
如果你仍然需要“Catch”這個迭代器中的錯誤,那么你可以這樣做:
public IEnumerable TransformData(List<string> data) { string text; foreach (string item in data) { try { text = PrepareDataRow(item); } catch (Exception ex) { Console.Error.WriteLine(ex.Message); continue; } yield return text; } }
yield break類似于break運算符,它只在迭代器中使用。這是一個小例子:
IEnumerable<int> GetNumbers() { int i = 0; while (true) { if (i = 5) yield break; yield return i++; } }
從示例中可以清楚地看出,當達到5的值時,迭代器將結束,但在此之前它將正確返回值。
總結一下,什么時候應該使用yield return?
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn