最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解C#中yield關(guān)鍵字的用法

 更新時間:2023年07月25日 10:31:49   作者:橙子家  
yield?關(guān)鍵字的用途是把指令推遲到程序?qū)嶋H需要的時候再執(zhí)行,這個特性允許我們更細致地控制集合每個元素產(chǎn)生的時機,那么下面就一起來看下怎么用?yield?關(guān)鍵字吧

〇、前言

yield 關(guān)鍵字的用途是把指令推遲到程序?qū)嶋H需要的時候再執(zhí)行,這個特性允許我們更細致地控制集合每個元素產(chǎn)生的時機。

對于一些大型集合,加載起來比較耗時,此時最好是先返回一個來讓系統(tǒng)持續(xù)展示目標內(nèi)容。類似于在餐館吃飯,肯定是做好一個菜就上桌了,而不會全部的菜都做好一起上。

另外還有一個好處是,可以提高內(nèi)存使用效率。當(dāng)我們有一個方法要返回一個集合時,而作為方法的實現(xiàn)者我們并不清楚方法調(diào)用者具體在什么時候要使用該集合數(shù)據(jù)。如果我們不使用 yield 關(guān)鍵字,則意味著需要把集合數(shù)據(jù)裝載到內(nèi)存中等待被使用,這可能導(dǎo)致數(shù)據(jù)在內(nèi)存中占用較長的時間。

下面就一起來看下怎么用 yield 關(guān)鍵字吧。

一、yield 關(guān)鍵字的使用

1.1 yield return:在迭代中一個一個返回待處理的值

如下示例,循環(huán)輸出小于 9 的偶數(shù),并記錄執(zhí)行任務(wù)的線程 ID:

class Program
{
    static async Task Main(string[] args)
    {
        foreach (int i in ProduceEvenNumbers(9))
        {
            ConsoleExt.Write($"{i}-Main");
        }
        ConsoleExt.Write($"--Main-循環(huán)結(jié)束");
        Console.ReadLine();
    }
    static IEnumerable<int> ProduceEvenNumbers(int upto)
    {
        for (int i = 0; i <= upto; i += 2)
        {
            ConsoleExt.Write($"{i}-ProduceEvenNumbers");
            yield return i;
            ConsoleExt.Write($"{i}-ProduceEvenNumbers-yielded");
        }
        ConsoleExt.Write($"--ProduceEvenNumbers-循環(huán)結(jié)束");
    }
}
public static class ConsoleExt
{
    public static void Write(object message)
    {
        Console.WriteLine($"(Time: {DateTime.Now.ToString("HH:mm:ss.ffffff")},  Thread {Thread.CurrentThread.ManagedThreadId}): {message} ");
    }
    public static void WriteLine(object message)
    {
        Console.WriteLine($"(Time: {DateTime.Now.ToString("HH:mm:ss.ffffff")},  Thread {Thread.CurrentThread.ManagedThreadId}): {message} ");
    }
    public static async void WriteLineAsync(object message)
    {
        await Task.Run(() => Console.WriteLine($"(Time: {DateTime.Now.ToString("HH:mm:ss.ffffff")},  Thread {Thread.CurrentThread.ManagedThreadId}): {message} "));
    }
}

輸出結(jié)果如下,可見整個循環(huán)是單線程運行,ProduceEvenNumbers()生產(chǎn)一個,然后Main()就操作一個,Main() 執(zhí)行一次操作后,線程返回生產(chǎn)線,繼續(xù)沿著 return 往后執(zhí)行;生產(chǎn)線循環(huán)結(jié)束后,Main() 也接著結(jié)束:

1.2 yield break:標識迭代中斷

如下示例代碼,通過條件中斷循環(huán):

class Program
{
    static void Main()
    {
        ConsoleExt.Write(string.Join(" ", TakeWhilePositive(new[] { 2, 3, 4, 5, -1, 3, 4 })));
        ConsoleExt.Write(string.Join(" ", TakeWhilePositive(new[] { 9, 8, 7 })));
        Console.ReadLine();
    }
    static IEnumerable<int> TakeWhilePositive(IEnumerable<int> numbers)
    {
        foreach (int n in numbers)
        {
            if (n > 0) // 遇到負數(shù)就中斷循環(huán)
            {
                yield return n;
            }
            else
            {
                yield break;
            }
        }
    }
}
public static class ConsoleExt
{
    public static void Write(object message)
    {
        Console.WriteLine($"(Time: {DateTime.Now.ToString("HH:mm:ss.ffffff")},  Thread {Thread.CurrentThread.ManagedThreadId}): {message} ");
    }
    public static void WriteLine(object message)
    {
        Console.WriteLine($"(Time: {DateTime.Now.ToString("HH:mm:ss.ffffff")},  Thread {Thread.CurrentThread.ManagedThreadId}): {message} ");
    }
    public static async void WriteLineAsync(object message)
    {
        await Task.Run(() => Console.WriteLine($"(Time: {DateTime.Now.ToString("HH:mm:ss.ffffff")},  Thread {Thread.CurrentThread.ManagedThreadId}): {message} "));
    }
}

輸出結(jié)果,第一個數(shù)組中第五個數(shù)為負數(shù),因此至此就中斷循環(huán),包括它自己之后的數(shù)字不再返回:

1.3 返回類型為 IAsyncEnumerable<T> 的異步迭代器

 實際上,不僅可以像前邊示例中那樣返回類型為 IEnumerable<T>,還可以使用 IAsyncEnumerable<T> 作為迭代器的返回類型,使得迭代器支持異步。

 如下示例代碼,使用 await foreach 語句對迭代器的結(jié)果進行異步迭代:(關(guān)于 await foreach 還有另外一個示例可參考 3.2 await foreach() 示例

class Program
{
    public static async Task Main()
    {
        await foreach (int n in GenerateNumbersAsync(5))
        {
            ConsoleExt.Write(n);
        }
        Console.ReadLine();
    }
    static async IAsyncEnumerable<int> GenerateNumbersAsync(int count)
    {
        for (int i = 0; i < count; i++)
        {
            yield return await ProduceNumberAsync(i);
        }
    }
    static async Task<int> ProduceNumberAsync(int seed)
    {
        await Task.Delay(1000);
        return 2 * seed;
    }
}
public static class ConsoleExt
{
    public static void Write(object message)
    {
        Console.WriteLine($"(Time: {DateTime.Now.ToString("HH:mm:ss.ffffff")},  Thread {Thread.CurrentThread.ManagedThreadId}): {message} ");
    }
    public static void WriteLine(object message)
    {
        Console.WriteLine($"(Time: {DateTime.Now.ToString("HH:mm:ss.ffffff")},  Thread {Thread.CurrentThread.ManagedThreadId}): {message} ");
    }
    public static async void WriteLineAsync(object message)
    {
        await Task.Run(() => Console.WriteLine($"(Time: {DateTime.Now.ToString("HH:mm:ss.ffffff")},  Thread {Thread.CurrentThread.ManagedThreadId}): {message} "));
    }
}

輸出結(jié)果如下,可見輸出的結(jié)果有不同線程執(zhí)行:

1.4 迭代器的返回類型可以是 IEnumerator<T> 或 IEnumerator

以下示例代碼,通過實現(xiàn) IEnumerable<T> 接口、GetEnumerator 方法,返回類型為 IEnumerator<T>,來展現(xiàn) yield 關(guān)鍵字的一個用法:

class Program
{
    public static void Main()
    {
        var ints = new int[] { 1, 2, 3 };
        var enumerable = new MyEnumerable<int>(ints);
        foreach (var item in enumerable)
        {
            Console.WriteLine(item);
        }
        Console.ReadLine();
    }
}
public class MyEnumerable<T> : IEnumerable<T>
{
    private T[] items;
    public MyEnumerable(T[] ts)
    {
        this.items = ts;
    }
    public void Add(T item)
    {
        int num = this.items.Length;
        this.items[num + 1] = item;
    }
    public IEnumerator<T> GetEnumerator()
    {
        foreach (var item in this.items)
        {
            yield return item;
        }
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

1.5 不能使用 yield 的情況

1.yield return 不能套在 try-catch 中;

2.yield break 不能放在 finally 中;

3.yield 不能用在帶有 in、ref 或 out 參數(shù)的方法;

4.yield 不能用在 Lambda 表達式和匿名方法;

5.yield 不能用在包含不安全的塊(unsafe)的方法。

https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/yield 

二、使用 yield 關(guān)鍵字實現(xiàn)惰性枚舉

在 C# 中,可以使用 yield 關(guān)鍵字來實現(xiàn)惰性枚舉。惰性枚舉是指在使用枚舉值時,只有在真正需要時才會生成它們,這可以提高程序的性能,因為在不需要使用枚舉值時,它們不會被生成或存儲在內(nèi)存中。

當(dāng)然對于簡單的枚舉,實際上還沒普通的 List<T> 有優(yōu)勢,因為取枚舉值也會對性能有損耗,所以只針對處理大型集合或延遲加載數(shù)據(jù)才能看到效果。

下面是一個簡單示例,展示了如何使用 yield 關(guān)鍵字來實現(xiàn)惰性枚舉:

public static IEnumerable<int> enumerableFuc()
{
    yield return 1;
    yield return 2;
    yield return 3;
}
// 使用惰性枚舉
foreach (var number in enumerableFuc())
{
    Console.WriteLine(number);
}

在上面的示例中,GetNumbers() 方法通過yield關(guān)鍵字返回一個 IEnumerable 對象。當(dāng)我們使用 foreach 循環(huán)迭代這個對象時,每次循環(huán)都會調(diào)用 MoveNext() 方法,并執(zhí)行到下一個 yield 語句處,返回一個元素。這樣就實現(xiàn)了按需生成枚舉的元素,而不需要一次性生成所有元素。

三、通過 IL 代碼看 yield 的原理

類比上一章節(jié)的示例代碼,用 while 循環(huán)代替 foreach 循環(huán),發(fā)現(xiàn)我們雖然沒有實現(xiàn) GetEnumerator(),也沒有實現(xiàn)對應(yīng)的 IEnumerator 的 MoveNext() 和 Current 屬性,但是我們?nèi)匀荒苷J褂眠@些函數(shù)。

static async Task Main(string[] args)
{
    // 用 while (enumerator.MoveNext()) 
    // 代替 foreach(int item in enumerableFuc())
    IEnumerator<int> enumerator = enumerableFuc().GetEnumerator();
    while (enumerator.MoveNext())
    {
        int current = enumerator.Current;
        Console.WriteLine(current);
    }
    Console.ReadLine();
}
// 一個返回類型為 IEnumerable<int>,其中包含三個 yield return
public static IEnumerable<int> enumerableFuc()
{
    Console.WriteLine("enumerableFuc-yield 1");
    yield return 1;
    Console.WriteLine("enumerableFuc-yield 2");
    yield return 2;
    Console.WriteLine("enumerableFuc-yield 3");
    yield return 3;
}

輸出的結(jié)果:

下面試著簡單看一下 Program 類的源碼

源碼如下,除了明顯的 Main() 和 enumerableFuc() 兩個函數(shù)外,反編譯的時候自動生成了一個新的類 '<enumerableFuc>d__1'。

注:反編譯時,語言選擇:“IL with C#”,有助于理解。

然后看自動生成的類的實現(xiàn),發(fā)現(xiàn)它繼承了 IEnumerable、IEnumerable<T>、IEnumerator、IEnumerator<T>,也實現(xiàn)了MoveNext()、Reset()、GetEnumerator()、Current 屬性,這時我們應(yīng)該可以確認,這個新的類,就是我們雖然沒有實現(xiàn)對應(yīng)的 IEnumerator 的 MoveNext() 和 Current 屬性,但是我們?nèi)匀荒苷J褂眠@些函數(shù)的原因了。

然后再具體看下 MoveNext() 函數(shù),根據(jù)輸出的備注字段,也能清晰的看到迭代過程,下圖中紫色部分:

下邊是是第三、四次迭代,可以看到行標識可以對得上:

每次調(diào)用 MoveNext() 函數(shù)都會將“ <>1__state”加 1,一共進行了 4 次迭代,前三次返回 true,最后一次返回 false,代表迭代結(jié)束。這四次迭代對應(yīng)被 3 個 yield return 語句分成4部分的 enumberableFuc() 中的語句。

用 enumberableFuc() 來進行迭代的真實流程就是:

  • 運行 enumberableFuc() 函數(shù),獲取代碼自動生成的類的實例;
  • 接著調(diào)用 GetEnumberator() 函數(shù),將獲取的類自己作為迭代器,準備開始迭代;
  • 每次運行 MoveNext() “ <>1__state”增加 1,通過 switch 語句可以讓每次調(diào)用 MoveNext() 的時候執(zhí)行不同部分的代碼;
  • MoveNext() 返回 false,結(jié)束迭代。

這也就說明了,yield 關(guān)鍵字其實是一種語法糖,最終還是通過實現(xiàn) IEnumberable<T>、IEnumberable、IEnumberator<T>、IEnumberator 接口實現(xiàn)的迭代功能。

到此這篇關(guān)于詳解C#中yield關(guān)鍵字的用法的文章就介紹到這了,更多相關(guān)C# yield內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#實現(xiàn)Workstation相關(guān)功能過程

    C#實現(xiàn)Workstation相關(guān)功能過程

    本文提供了實現(xiàn)Workstation功能的C#代碼示例,涵蓋工作站管理、遠程控制、性能監(jiān)控和定時任務(wù)等核心模塊,代碼結(jié)構(gòu)清晰,可根據(jù)需要擴展,但使用時需注意網(wǎng)絡(luò)權(quán)限和異常處理
    2026-05-05
  • C#根據(jù)反射和特性實現(xiàn)ORM映射實例分析

    C#根據(jù)反射和特性實現(xiàn)ORM映射實例分析

    這篇文章主要介紹了C#根據(jù)反射和特性實現(xiàn)ORM映射的方法,實例分析了反射的原理、特性與ORM的實現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-04-04
  • 使用C#實現(xiàn)插入各種表格到Word文檔

    使用C#實現(xiàn)插入各種表格到Word文檔

    在許多企業(yè)應(yīng)用場景中,Word 文檔依舊是最常用的信息呈現(xiàn)與內(nèi)容輸出格式,下面將介紹在 C# 中如何以編程方式創(chuàng)建 Word 文檔、插入表格、設(shè)置樣式,并擴展到動態(tài)行列與嵌套表格等高級操作,希望對大家有所幫助
    2025-11-11
  • C#中如何在Excel工作表創(chuàng)建混合型圖表實例

    C#中如何在Excel工作表創(chuàng)建混合型圖表實例

    本篇文章主要介紹了C#中如何在Excel工作表創(chuàng)建混合型圖表實例,具有一定的參考價值,有需要的可以了解一下。
    2016-11-11
  • C#中實現(xiàn)向數(shù)組中動態(tài)添加元素

    C#中實現(xiàn)向數(shù)組中動態(tài)添加元素

    這篇文章主要介紹了C#中實現(xiàn)向數(shù)組中動態(tài)添加元素方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • c# unmanaged 約束的具體使用

    c# unmanaged 約束的具體使用

    本文主要介紹了c# unmanaged 約束的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-03-03
  • C# TimeSpan計算時間差的操作完整指南

    C# TimeSpan計算時間差的操作完整指南

    TimeSpan 是 .NET 中用于表示時間間隔或持續(xù)時間的重要結(jié)構(gòu)體,它提供了豐富的方法和屬性來處理時間跨度,下面我們就來看看C# TimeSpan計算時間差的完整操作吧
    2025-12-12
  • c# checked和unchecked關(guān)鍵字的使用

    c# checked和unchecked關(guān)鍵字的使用

    C#中的checked關(guān)鍵字用于啟用整數(shù)運算的溢出檢查,可以捕獲并拋出System.OverflowException異常,而unchecked關(guān)鍵字則禁用這種檢查,允許結(jié)果溢出,下面就來具體介紹一下
    2025-01-01
  • Unity制作游戲自定義按鍵詳解

    Unity制作游戲自定義按鍵詳解

    這篇文章主要介紹了在Unity中如何實現(xiàn)游戲自定義按鍵,文中的示例代碼講解詳細,對我們學(xué)習(xí)Unity有一定幫助,感興趣的可以跟隨小編學(xué)習(xí)一下
    2022-01-01
  • C#中類的異常處理詳解

    C#中類的異常處理詳解

    大家好,本篇文章主要講的是C#中類的異常處理詳解,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-02-02

最新評論

花垣县| 安国市| 鹤壁市| 大化| 梁山县| 远安县| 鄢陵县| 西吉县| 吉水县| 灵山县| 明光市| 六安市| 台东市| 玉田县| 灵山县| 托克逊县| 桃园市| 台东市| 车险| 鹤庆县| 吴江市| 永吉县| 广河县| 陈巴尔虎旗| 龙陵县| 砀山县| 恩施市| 邛崃市| 竹北市| 博乐市| 桦川县| 从化市| 新兴县| 安义县| 舟曲县| 元朗区| 安图县| 绥芬河市| 晴隆县| 建平县| 二连浩特市|