C# 中的屈 yield 介紹—它是什麼、如何使用以及何時有用
當您第一次接觸C#中的yield關鍵字時,可能會感到困惑。 它究竟如何運作? 應該在何時使用yield return,而不是傳統的return語句? 為了獲得完整的理解,我們將通過Tim Corey的優秀YouTube教學"C#中的Yield介紹 - 它是什麼,如何使用,以及什麼時候有用"進行詳細解釋。
在本指南中,我們將參考Tim影片中的特定時間點,以便於瀏覽,並包含實際範例展示yield如何改變您處理資料流、大型集合和延遲評估的方式。
C#中Yield關鍵字介紹
Tim首先介紹了yield關鍵字,並強調它對於首次遇到它的開發者來說常常令人困惑。他解釋了yield語句允許方法暫停執行、保留其狀態,然後在下一次調用時從中斷處繼續。 Tim強調理解yield對於高效處理資料至關重要,特別是在處理大型資料集或實現自定義迭代邏輯時。
建立簡單範例:類別Program和靜態Void Main
為了消除干擾,Tim在Visual Studio中創建了一個名為"YieldDemoApp"的簡單控制台應用程式。

Yield在C#中實際做了什麼
然後Tim深入理論。 在2:04,他描述了yield的行為:不再一次處理整個集合,yield語句保留了一個位置——就像把大拇指放進書中——以便執行可以暫停稍後恢復。
這種行為對於延遲執行至關重要,其中只有在需要時才會生成值,而不是預先計算所有內容。 Tim的描述清楚地奠定了理解yield return如何運作的基礎。
撰寫範例程式碼
在類別Program的靜態void Main方法中,他設置了基本的首尾訊息,例如"應用程式開始"和"應用程式結束",使用Console.WriteLine,有助於清晰地可視化稍後使用foreach循環的流程。
這個初始的程式碼範例專注於yield實現,而不涉及任何UI複雜性。
創建PersonModel類
為了示範,Tim創建了一個具有FirstName和LastName屬性以及構造函數的PersonModel類。 當創建PersonModel物件時,會打印一條訊息,指出初始化的用戶。 這有助於可視化對象是在何時創建,何時被消耗。

這個簡單的生成程式碼步驟為使用自定義迭代器做好了準備。
構建具有傳統列表返回的DataAccess類
在5:06,Tim轉至具有返回IEnumerable

該迭代器方法在迭代開始之前立即將所有對象加載到內存中,這是一個重要點,Tim稍後將其與使用yield進行對比。

演示列表的內存使用
在運行foreach循環後,Tim展示了所有三個用戶都是在第一次讀取元素之前創建的。 這突出了一個處理大集合或大型資料集時的潛在問題——高內存使用。

Tim解釋說,如果我們有一千個用戶,甚至只需要少數,我們也會創建一千個對象,導致內存分配效率低下。
更改為Yield Return進行延遲執行
在10:01,Tim修改GetPeople使用yield return,而不是創建臨時集合(列表)。 每個yield return語句都直接一次發出一個PersonModel。
方法中的這段關鍵程式碼允許延遲評估,只有在foreach需要時才生成下一個元素。
Tim還澄清,方法的返回類型必須是IEnumerable
除錯:編譯器如何生成Yield的程式碼
Tim使用斷點逐步檢查過程。 他表明在第一次MoveNext調用前,序列為空。 只有當foreach需要下一個值時,編譯器才會觸發迭代器塊並執行yield return num行,這會初始化並返回PersonModel。

Tim強調,編譯器在底層生成特別的狀態機來管理已暫停和已恢復的執行。
使用Yield Return的好處和效率
Tim解釋為何yield如此高效:
-
您可以一次檢索一條記錄。
-
您可以限制所需的int計數。
-
您可以避免將整個集合加載到記憶體中。
- 您提高了可擴展性,尤其是在處理大型檔案或資料流時。
通過使用LINQ的.Take(2),Tim演示了即使存在三個yield return語句,只有兩個對象被初始化——強調了延遲執行的實際作用。
實際範例:使用Yield的質數生成器
Tim在Generators類中建立了一個新的靜態IEnumerable
這個範例表明,yield在安全處理無限序列方面至關重要。
若沒有yield,程式碼將因無限制的記憶體消耗而崩潰。 然而,使用yield,延遲執行確保了數字是按需生成的。
使用Take()抓取質數
Tim然後展示如何安全地只抓取固定數量的質數:
var primeNumbers = Generators.GetPrimeNumbers().Take(10000);
var primeNumbers = Generators.GetPrimeNumbers().Take(10000);
這段程式碼寫在靜態void Main中,能有效抓取10,000個質數。
因為yield return逐一生成數字,所以記憶體使用量保持低。
自定義迭代:使用GetEnumerator和MoveNext
Tim更進一步解釋如何手動控制迭代。
他創建了var iterator = primeNumbers.GetEnumerator(),然後使用int i的for循環和調用iterator.MoveNext()手動抓取元素。
這種手動方法使自定義迭代成為可能——只有在需要時才請求下一個值,並顯示該方法精確地從上次暫停處恢復。
常見錯誤:在Yielded集合上使用ToList()
在36:45,Tim警告:將yield序列轉換為名單的.ToList()導致立即完全評估。
如果在無限序列上調用.ToList(),您有可能使您的應用程式崩潰。
Tim強調,yield return旨在進行延遲評估,調用.ToList()會打破這一模式,強迫完全記憶體實現。
在使用LINQ方法時,Tim建議謹慎考慮何時引入.ToList()。
總結:為何Yield是一個強大的工具
Tim以強調即使yield關鍵字會增加一點開銷(持有狀態機),但減少的記憶體使用和延遲評估的優勢,使其在處理以下情況時成為強大的工具:
-
大型資料集
-
大型檔案
-
自定義迭代器
-
無限序列
-
資料流。
- 延遲處理。
他建議在不同專案中練習yield,來加深對yield return的理解,並避免常見陷阱。
Tim最後邀請觀眾分享他們如何在實際代碼中使用yield。
結論
通過觀看Tim Corey的影片,我們看到yield關鍵字給C#帶來的巨大好處。 從創建自定義迭代器到高效管理大型集合,yield return允許函數返回更聰明且更具記憶體效能。 無論您是處理var number、var point、var reader、var connection,或者大型資料集,掌握yield可以顯著提升您的C#編碼技巧。
如果您還沒探索過使用yield,現在是透過簡單範例進行練習並更好地了解yield return在C#編譯器內部如何工作的最佳時機。 請務必查看Tim的官方YouTube頻道,以獲得更多有洞察力的影片。
