亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C#中yield關(guān)鍵字之從使用到原理分析

 更新時(shí)間:2025年06月17日 10:15:01   作者:終不似少年游啊  
這篇文章主要介紹了C#中yield關(guān)鍵字之從使用到原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

在 C# 編程中,yield關(guān)鍵字是一個(gè)強(qiáng)大且實(shí)用的語(yǔ)法糖,它主要用于簡(jiǎn)化迭代器的實(shí)現(xiàn)。通過(guò)yield,開發(fā)者可以用更簡(jiǎn)潔的代碼實(shí)現(xiàn)延遲執(zhí)行、按需生成數(shù)據(jù)的功能,尤其在處理大數(shù)據(jù)集合或自定義迭代邏輯時(shí)表現(xiàn)出色。

本文將從基礎(chǔ)概念入手,逐步深入到yield的底層實(shí)現(xiàn)原理,幫助讀者全面掌握這一重要特性。

一、yield 關(guān)鍵字的基本概念

在 C# 中,yield關(guān)鍵字主要用于定義迭代器方法,它有兩種形式:

  • yield return:返回一個(gè)值并暫停方法執(zhí)行,保存當(dāng)前狀態(tài)
  • yield break:終止迭代過(guò)程

下面通過(guò)一個(gè)簡(jiǎn)單示例對(duì)比使用yield和傳統(tǒng)方式實(shí)現(xiàn)迭代的差異:

// 傳統(tǒng)方式:返回完整集合
public static List<int> GetNumbers()
{
    List<int> numbers = new List<int>();
    numbers.Add(1);
    numbers.Add(2);
    numbers.Add(3);
    return numbers;
}

// 使用yield:延遲生成值
public static IEnumerable<int> GetNumbers()
{
    yield return 1;
    yield return 2;
    yield return 3;
}

從這個(gè)例子可以看出,使用yield后代碼變得更加簡(jiǎn)潔,不需要顯式創(chuàng)建和管理集合。

二、yield 的核心特性:延遲執(zhí)行與狀態(tài)管理

1. 延遲執(zhí)行機(jī)制

yield最顯著的特性是延遲執(zhí)行(Lazy Evaluation)。當(dāng)調(diào)用包含yield的方法時(shí),方法體并不會(huì)立即執(zhí)行,而是返回一個(gè)實(shí)現(xiàn)了IEnumerable<T>接口的迭代器對(duì)象。

只有當(dāng)客戶端代碼通過(guò)foreach或直接調(diào)用MoveNext()方法時(shí),方法體才會(huì)真正執(zhí)行。

下面的示例展示了延遲執(zhí)行的效果:

public static IEnumerable<int> GetNumbers()
{
    Console.WriteLine("開始執(zhí)行");
    yield return 1;
    Console.WriteLine("返回了第一個(gè)值");
    yield return 2;
    Console.WriteLine("返回了第二個(gè)值");
    yield return 3;
    Console.WriteLine("返回了第三個(gè)值");
}

// 調(diào)用代碼
var numbers = GetNumbers();
Console.WriteLine("迭代器已經(jīng)創(chuàng)建");

foreach (int number in numbers)
{
    Console.WriteLine($"獲取到值: {number}");
    Console.WriteLine("--------");
}

輸出結(jié)果如下:

迭代器已經(jīng)創(chuàng)建
開始執(zhí)行
獲取到值: 1
--------
返回了第一個(gè)值
獲取到值: 2
--------
返回了第二個(gè)值
獲取到值: 3
--------
返回了第三個(gè)值

從輸出可以看出,直到第一次調(diào)用MoveNext()(通過(guò)foreach觸發(fā))時(shí),方法體才開始執(zhí)行,并且每次yield后會(huì)暫停執(zhí)行,等待下一次請(qǐng)求。

2. 自動(dòng)狀態(tài)管理

yield方法會(huì)自動(dòng)保存局部變量的狀態(tài)。每次調(diào)用MoveNext()時(shí),方法會(huì)從上一次yield的位置繼續(xù)執(zhí)行,而不是從頭開始。

這種狀態(tài)管理是由編譯器自動(dòng)實(shí)現(xiàn)的,開發(fā)者無(wú)需手動(dòng)維護(hù)。

三、yield 的底層實(shí)現(xiàn)原理

1. 編譯器魔法:狀態(tài)機(jī)轉(zhuǎn)換

當(dāng)編譯器遇到包含yield的方法時(shí),會(huì)進(jìn)行以下轉(zhuǎn)換:

  1. 創(chuàng)建狀態(tài)機(jī)類:生成一個(gè)實(shí)現(xiàn)了IEnumerable<T>IEnumerator<T>接口的嵌套類
  2. 分解方法體:將原方法體分解為多個(gè)狀態(tài),每個(gè)yield return成為一個(gè)狀態(tài)轉(zhuǎn)換點(diǎn)
  3. 實(shí)現(xiàn)狀態(tài)管理:通過(guò)狀態(tài)變量(如整數(shù))記錄當(dāng)前執(zhí)行位置,保存所有局部變量

下面是一個(gè)簡(jiǎn)化的狀態(tài)機(jī)實(shí)現(xiàn)示例(實(shí)際編譯器生成的代碼更復(fù)雜):

private sealed class IteratorStateMachine : IEnumerator<int>, IEnumerable<int>
{
    // 狀態(tài)標(biāo)識(shí)
    private int state;
    // 當(dāng)前返回值
    private int current;
    
    // 每次調(diào)用GetEnumerator()時(shí)創(chuàng)建新實(shí)例
    public IteratorStateMachine(int state) => this.state = state;
    
    // IEnumerable接口實(shí)現(xiàn)
    public IEnumerator<int> GetEnumerator() => this;
    object IEnumerator.Current => current;
    public int Current => current;
    
    // 核心方法:控制狀態(tài)轉(zhuǎn)換
    public bool MoveNext()
    {
        switch (state)
        {
            case 0: // 初始狀態(tài)
                state = -1; // 默認(rèn)標(biāo)記為完成
                current = 1; // 設(shè)置第一個(gè)返回值
                state = 1;   // 跳轉(zhuǎn)到第一個(gè)yield后的狀態(tài)
                return true;
                
            case 1: // 第一個(gè)yield后
                state = -1;
                current = 2;
                state = 2;
                return true;
                
            case 2: // 第二個(gè)yield后
                state = -1;
                current = 3;
                state = 3;
                return true;
                
            case 3: // 第三個(gè)yield后
                return false; // 迭代結(jié)束
                
            default:
                return false;
        }
    }
    
    // 其他接口方法
    public void Dispose() => state = -1;
    public void Reset() => throw new NotSupportedException();
}

2. 執(zhí)行流程解析

  1. 調(diào)用方法時(shí):返回狀態(tài)機(jī)的一個(gè)實(shí)例,初始狀態(tài)為 0
  2. 第一次調(diào)用 MoveNext ():執(zhí)行狀態(tài) 0 的代碼,設(shè)置 current 值,更新狀態(tài)為 1
  3. 后續(xù)調(diào)用 MoveNext ():根據(jù)當(dāng)前狀態(tài)執(zhí)行對(duì)應(yīng)的代碼片段,直到狀態(tài)變?yōu)橥瓿桑?1)

3. 資源管理與異常處理

  • using 語(yǔ)句:如果yield方法中使用了using語(yǔ)句,狀態(tài)機(jī)的Dispose()方法會(huì)確保資源被正確釋放
  • 異常處理yield方法中不能有try-catch塊(因?yàn)闋顟B(tài)機(jī)無(wú)法跨yield保存異常上下文),但可以有try-finally

四、yield 的典型應(yīng)用場(chǎng)景

1. 大數(shù)據(jù)集合處理

當(dāng)處理大量數(shù)據(jù)時(shí),yield可以顯著節(jié)省內(nèi)存,實(shí)現(xiàn)按需加載:

public static IEnumerable<DataItem> LoadLargeData()
{
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        using (var command = new SqlCommand(query, connection))
        {
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    yield return new DataItem
                    {
                        Id = reader.GetInt32(0),
                        Name = reader.GetString(1)
                    };
                }
            }
        }
    }
}

2. 自定義迭代邏輯

通過(guò)yield可以輕松實(shí)現(xiàn)復(fù)雜的迭代邏輯,例如過(guò)濾、轉(zhuǎn)換數(shù)據(jù):

public class MyCollection
{
    private readonly int[] _items;

    public MyCollection(int[] items)
    {
        _items = items;
    }

    public IEnumerable<int> EvenNumbers()
    {
        foreach (var item in _items)
        {
            if (item % 2 == 0)
            {
                yield return item;
            }
        }
    }
}

3. 無(wú)限序列生成

生成無(wú)限序列(如斐波那契數(shù)列)時(shí),yield是理想選擇:

public static IEnumerable<int> Fibonacci()
{
    int a = 0, b = 1;
    while (true)
    {
        yield return a;
        int temp = a;
        a = b;
        b = temp + b;
    }
}

五、使用 yield 的注意事項(xiàng)

  1. 返回類型限制yield方法的返回類型必須是IEnumerable<T>IEnumerator<T>
  2. 不能在匿名方法中使用yield不能用于 lambda 表達(dá)式或匿名方法
  3. 異常處理限制:不能有try-catch塊,但可以有try-finally
  4. 性能考慮:多次枚舉可能導(dǎo)致重復(fù)計(jì)算,可考慮使用ToList()緩存結(jié)果

六、總結(jié)

yield關(guān)鍵字是 C# 語(yǔ)言中一個(gè)強(qiáng)大的語(yǔ)法糖,它通過(guò)狀態(tài)機(jī)模式自動(dòng)實(shí)現(xiàn)了復(fù)雜的迭代器邏輯,讓開發(fā)者可以用更簡(jiǎn)潔的代碼實(shí)現(xiàn)延遲執(zhí)行和狀態(tài)管理。理解yield的底層原理有助于更高效地使用它,并避免潛在的性能問(wèn)題。

在實(shí)際開發(fā)中,yield特別適合處理大數(shù)據(jù)集合、實(shí)現(xiàn)自定義迭代邏輯和生成無(wú)限序列等場(chǎng)景。通過(guò)合理使用yield,可以顯著提升代碼的可讀性和性能。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • C#常用知識(shí)點(diǎn)簡(jiǎn)單回顧(有圖有真相)

    C#常用知識(shí)點(diǎn)簡(jiǎn)單回顧(有圖有真相)

    C#知識(shí)點(diǎn)記錄編程的點(diǎn)點(diǎn)滴滴,本文整理了一些(傳值調(diào)用與引用調(diào)用/打印三角形/遞歸求階乘/多態(tài)性..等等),感興趣的朋友可以了解下的,不要走開(有圖有真相)
    2013-01-01
  • C#根據(jù)反射和特性實(shí)現(xiàn)ORM映射實(shí)例分析

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

    這篇文章主要介紹了C#根據(jù)反射和特性實(shí)現(xiàn)ORM映射的方法,實(shí)例分析了反射的原理、特性與ORM的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-04-04
  • C# 實(shí)現(xiàn)PPT 每一頁(yè)轉(zhuǎn)成圖片過(guò)程解析

    C# 實(shí)現(xiàn)PPT 每一頁(yè)轉(zhuǎn)成圖片過(guò)程解析

    這篇文章主要介紹了C# 實(shí)現(xiàn)PPT 每一頁(yè)轉(zhuǎn)成圖片過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • C#實(shí)現(xiàn)常見時(shí)間格式

    C#實(shí)現(xiàn)常見時(shí)間格式

    這篇文章介紹了C#實(shí)現(xiàn)常見時(shí)間格式的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • C#中LinkedList<T>的存儲(chǔ)結(jié)構(gòu)詳解

    C#中LinkedList<T>的存儲(chǔ)結(jié)構(gòu)詳解

    這篇文章主要介紹了深度解析C#中LinkedList<T>的存儲(chǔ)結(jié)構(gòu),本文將從鏈表的基礎(chǔ)特性、C#中LinkedList的底層實(shí)現(xiàn)邏輯,.NET的不同版本對(duì)于Queue的不同實(shí)現(xiàn)方式的原因分析等幾個(gè)視角進(jìn)行簡(jiǎn)單的解讀,需要的朋友可以參考下
    2023-12-12
  • C#生成帶注釋的dll并引用實(shí)現(xiàn)

    C#生成帶注釋的dll并引用實(shí)現(xiàn)

    本文主要介紹了C#生成帶注釋的dll并引用實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • C#實(shí)現(xiàn)將javascript文件編譯成dll文件的方法

    C#實(shí)現(xiàn)將javascript文件編譯成dll文件的方法

    這篇文章主要介紹了C#實(shí)現(xiàn)將javascript文件編譯成dll文件的方法,涉及C#編譯生成dll動(dòng)態(tài)鏈接庫(kù)文件的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-11-11
  • 使用淘寶ip地址庫(kù)查ip的示例

    使用淘寶ip地址庫(kù)查ip的示例

    這篇文章主要介紹了使用淘寶ip地址庫(kù)查ip的示例,需要的朋友可以參考下
    2014-03-03
  • c# RSA非對(duì)稱加解密及XML&PEM格式互換方案

    c# RSA非對(duì)稱加解密及XML&PEM格式互換方案

    這篇文章主要介紹了c# RSA非對(duì)稱加解密及XML&PEM格式互換方案,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下
    2020-12-12
  • C#中的多線程多參數(shù)傳遞詳解

    C#中的多線程多參數(shù)傳遞詳解

    第一種解決方案的原理是:將線程執(zhí)行的方法和參數(shù)都封裝到一個(gè)類里面。通過(guò)實(shí)例化該類,方法就可以調(diào)用屬性來(lái)實(shí)現(xiàn)間接的類型安全地傳遞多個(gè)參數(shù)
    2014-01-01

最新評(píng)論