簡體   English   中英

需要幫助理解IEnumerable中的C#yield

[英]Need help understanding C# yield in IEnumerable

我正在閱讀C#2010 Accelerated。 我不知道什么是yield

調用GetEnumerator時,包含yield語句的方法中的代碼實際上並未在該時間點執行。 相反,編譯器生成枚舉器類,該類包含yield塊代碼

public IEnumerator<T> GetEnumerator() { 
    foreach( T item in items ) { 
        yield return item; 
    } 
} 

我也讀了一些幫助理解“產量”

yield是一個懶惰的數據生成器,只在檢索到第一個后生成另一個項目,而返回一個列表將一次性返回所有內容。

這是否意味着每次調用GetEnumerator都會從集合中獲得1個項目? 所以第一個電話我得到第一個項目,第二個,我得到第二個,依此類推......?

想到它的最佳方式是當你第一次從IEnumerator請求一個項目時(例如在foreach ),它開始通過該方法運行,當它達到yield return它會暫停執行並返回該項目供你使用你的foreach 然后你請求下一個項目,它恢復它離開的代碼並重復循環,直到它遇到yield break或方法結束。

public IEnumerator<string> enumerateSomeStrings()
{
    yield return "one";
    yield return "two";
    var array = new[] { "three", "four" }
    foreach (var item in array)
        yield return item;
    yield return "five";
}

看一下IEnumerator<T>接口; 這很可能是為了澄清正在發生的事情。 編譯器將您的代碼轉換為實現IEnumerable<T>IEnumerator<T> 對GetEnumerator()的調用只返回類本身。

該實現基本上是一個狀態機,對於每次調用MoveNext(),它執行代碼直到下一個yield return ,然后將Current設置為返回值。 foreach循環使用此枚舉器遍歷枚舉項,在循環的每次迭代之前調用MoveNext()。 編譯器在這里做了一些非常酷的事情,使yield return語言中最強大的構造之一。 從程序員的角度來看,這只是一種根據要求懶惰退貨的簡單方法。

是的,這是正確的,來自MSDN的例子說明了如何使用它

public class List
{
    //using System.Collections;
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
        {
            result = result * number;
            yield return result;
        }
    }

    static void Main()
    {
        // Display powers of 2 up to the exponent 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }
}
/*
Output:
2 4 8 16 32 64 128 256 
*/

如果我理解你的問題是正確的,那么你的理解是錯誤的,我很害怕。 yield語句(yield return和yield break)是一個非常聰明的編譯器技巧。 您方法中的代碼實際上被編譯為實現IEnumerable的類。 該類的一個實例是該方法將返回的內容。 讓我們在調用ins.GetEnumerator()時調用實例'ins',你會得到一個IEnumerator,當每個調用MoveNext()生成集合中的下一個元素時(yield返回負責這個部分),當序列沒有更多的元素時(例如遇到yield break)MoveNext()返回false,進一步調用會導致異常。 因此,GetEnumerator的調用不是生成(下一個)元素而是調用MoveNext

理解yield關鍵字的簡單方法是,當使用yield return關鍵字返回時,我們不需要額外的類來保存迭代的結果。 通常,當我們遍歷集合並想要返回結果時,我們使用集合對象來保存結果。 我們來看看例子吧。

public static List Multiplication(int number,int times)

    {
        List<int> resultList = new List<int>();
        int result = number;
        for(int i=1;i<=times;i++)
        {
            result=number*i;
            resultList.Add(result);
        }
        return resultList;
    }

static void Main(string [] args)

    {
        foreach(int i in Multiplication(2,10))
        {
            Console.WriteLine(i);
        }
        Console.ReadKey();
    }

在上面的例子中,我想返回2次乘法的結果。 所以我創建一個方法乘法,它返回2倍的乘法,並將結果存儲在列表中,當我的main方法調用乘法方法時,控件循環遍歷循環十次並將結果結果存儲在列表中。 這是沒有使用收益率回報。 假設我想使用yield return執行此操作,它看起來像

public static IEnumerable Multiplication(int number,int times)

          {
        int result = number;
        for(int i=1;i<=times;i++)
        {
            result=number*i;
            yield return result;
        }
    }

static void Main(string [] args)

    {
        foreach(int i in Multiplication(2,10))
        {
            Console.WriteLine(i);
        }
        Console.ReadKey();
    }

現在在Multiplication方法中有輕微的變化,返回類型是IEnumerable,並且沒有其他列表來保存結果,因為使用Yield返回類型必須是IEnumerable或IEnumerator,並且由於Yield提供有狀態迭代,我們不需要額外的類來保存結果。 因此在上面的例子中,當從Main方法調用Multiplication方法時,它計算第一次迭代的結果並將結果返回到main方法並返回到循環並計算第二次迭代的結果並將結果返回給main方法這樣,Yield在每次迭代中將結果返回到調用方法一個接一個。還有其他關鍵字中斷與Yield結合使用會導致迭代停止。 例如,在上面的例子中,如果我想計算乘法只有一半的次數(10/2 = 5),那么方法如下所示:

public static IEnumerable Multiplication(int number,int times)

    {
        int result = number;
        for(int i=1;i<=times;i++)
        {
            result=number*i;
            yield return result;
            if (i == times / 2)
                yield break;
        }

    }

這個方法現在將導致乘以2,5倍。希望這有助於您理解Yield的概念。 有關更多信息,請訪問http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

看起來你明白了。

如你所描述的那樣,在你的類的GetEnumerator使用yield ,這樣你就可以編寫如下代碼:

foreach (MyObject myObject in myObjectCollection)
{
    // Do something with myObject
}

通過從第一個調用返回第一個項目,從第二個調用返回第二個項目,依此類推,您可以遍歷集合中的所有元素。

yieldMyObjectCollection定義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM