## 引言 **yield**關鍵字是 C# 中的一種語言特性,用於在枚舉器中簡化迭代器的實現。它使得開發人員可以通過定義自己的迭代器來簡化代碼,而不必手動實現 IEnumerable 和 IEnumerator 介面。 使用 `yield` 關鍵字,可以將迭代器中的值一次一個地返回,而不必使用一個 ...
引言
yield關鍵字是 C# 中的一種語言特性,用於在枚舉器中簡化迭代器的實現。它使得開發人員可以通過定義自己的迭代器來簡化代碼,而不必手動實現 IEnumerable 和 IEnumerator 介面。
使用 yield
關鍵字,可以將迭代器中的值一次一個地返回,而不必使用一個集合對象存儲所有的值。當執行到yield return
語句時,代碼將會暫停執行,將返回值傳遞給迭代器的調用者,並將迭代器的狀態保存下來。當下一次調用MoveNext
方法時,代碼將從之前的暫停點繼續執行,直到遇到下一個yield return
語句或者迭代器結束。
接下來探索一下 yield
的三種玩法:
初級
例如通過 yield
創建出一個 IEnumerable
以供 foreach
遍歷,代碼如下:
internal class Program
{
static void Main(string[] args)
{
foreach (int i in EvenSequence(5, 18))
{
Console.Write(i + " ");
}
Console.ReadKey();
}
static IEnumerable<int> EvenSequence(int start, int end)
{
for (int i = start; i <= end; i++)
{
if (i % 2 == 0)
{
yield return i;
}
}
}
}
在上面的代碼中,我們定義了一個名為EvenSequence
的方法,它返回一個實現了IEnumerable<int>
介面的對象。在EvenSequence
方法中,我們使用yield return
語句來返回每個偶數值,併在每次暫停後保存方法的狀態。
在Main
方法中,我們使用foreach
迴圈語句來遍歷EvenSequence
方法返回的集合對象,並輸出每個偶數值。由於我們使用了 yield 關鍵字,即使我們沒有顯式地實現IEnumerable
和IEnumerator
介面,也能夠遍歷集合對象。
進階
另一個方面,非同步編程中也常常使用yield來創建非同步生成器。使用yield創建非同步生成器可以讓我們輕鬆地以非同步方式生成一系列值,而無需顯式地管理非同步狀態。如以下代碼:
public class Program
{
public static void Main(string[] args)
{
AsynchronousIterate();
Console.ReadLine();
}
public static async void AsynchronousIterate()
{
await foreach (var number in GenerateNumbersAsync())
{
Console.WriteLine(number);
}
}
public static async IAsyncEnumerable<int> GenerateNumbersAsync()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(1000);
yield return i;
}
}
}
上述代碼定義了一個名為 GenerateNumbersAsync
的非同步方法,該方法返回一個 IAsyncEnumerable<int>
類型的對象。在方法體中,我們使用一個 for
迴圈來生成一系列整數,併在每次迭代中非同步等待1秒鐘。緊接著,我們使用 yield return
語句將生成的整數返回給調用方。 要註意調用時使用非同步迭代器(具有 await
關鍵字的foreach
)來進行遍歷>。
上述輸出則是一秒輸出一個結果,知道全部輸出。
進階舉例場景
現在有這一樣一個場景,有一大缸水,你手裡有一個水瓢,,現在需要你把水缸里的水,全部移到另一個水缸。
前提條件:現在不知道缸里由具體多少水,也無法確定一瓢能舀多少,也無法確定你每一次舀水操作需要多長時間。
現在寫一段代碼,模擬這個過程。
擬定前提條件,如下代碼:
static Random Random = new Random();
/// <summary>
/// 假設水缸能盛水90~100
/// </summary>
public static int GetOneWaterTankCapacity()
{
return Random.Next(150, 200);
}
/// <summary>
/// 假設水瓢一次只能舀水2~5
/// </summary>
public static int GetOneWaterLadleCapacity()
{
return Random.Next(2, 5);
}
/// <summary>
/// 模擬舀水所消耗的時間(0.5s~2S)
/// </summary>
/// <returns></returns>
public static async Task<int> ScoopingWater()
{
await Task.Delay(Random.Next(500, 2000));
return GetOneWaterLadleCapacity();
}
開始舀水,代碼如下:
/// <summary>
/// 開始搬水
/// </summary>
/// <returns></returns>
public static async void MoveWater()
{
//先確定缸里有多少水
var WaterTankCapacity = GetOneWaterTankCapacity();
await foreach (var item in CreateTasks(WaterTankCapacity))
{
Console.WriteLine($"這一瓢舀水量:{item.Result}\t{DateTime.Now}");
}
Console.WriteLine($"水全部舀完~\t{DateTime.Now}");
}
private static async IAsyncEnumerable<Task<int>> CreateTasks(int waterTankCapacity)
{
int totle = 0;
while (totle < waterTankCapacity)
{
Task<int> someWater = ScoopingWater();
yield return someWater;
totle += await someWater;
}
}
最終會每次隨機舀水,花費隨機時間,水全部舀完。
輸出:
這一瓢舀水量:4 2023/5/11 23:06:29
這一瓢舀水量:3 2023/5/11 23:06:30
這一瓢舀水量:2 2023/5/11 23:06:32
這一瓢舀水量:2 2023/5/11 23:06:34
這一瓢舀水量:3 2023/5/11 23:06:35
這一瓢舀水量:4 2023/5/11 23:06:36
這一瓢舀水量:2 2023/5/11 23:06:37
....
水全部舀完~ 2023/5/11 23:07:27
通過上文例子,可以更深入理解 yield
創建的非同步生成器。
總結
- 使用
yield
關鍵字可以將一個方法轉換為一個返回可枚舉對象或迭代器的方法,而不必手動實現 IEnumerable 和 IEnumerator 介面 - 使用
yield
來創建非同步生成器,在某些場景下可以實現更高效、可靠的非同步編程。
作者: Niuery Daily
出處: https://www.cnblogs.com/pandefu/>
關於作者:.Net Framework,.Net Core ,WindowsForm,WPF ,控制項庫,多線程
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出 原文鏈接,否則保留追究法律責任的權利。 如有問題, 可郵件咨詢。