簡體   English   中英

IEnumerable怎么樣? <T> 在后台工作

[英]How does IEnumerable<T> work in background

我正在徘徊IEnumerable<T>接口的更深入的功能。

基本上,它作為執行中的中間步驟。 例如,如果你寫:

IEnumerable<int> temp = new int[]{1,2,3}.Select(x => 2*x);

在使用temp進行某些操作(例如List<int> list = temp.ToList() )之前,不會計算(枚舉) Select函數的結果。

然而,讓我感到困惑的是,由於IEnumerable<T>是一個接口,根據定義,它不能被實例化。 那么,實際項目(在示例2*x項目中)所在的集合是什么?

而且,如果我們要編寫IEnumerable<int> temp = Enumerable.Repeat(1, 10); ,什么是存儲1s的底層集合(數組,列表,其他東西)?

我似乎無法找到關於此接口的實際實現及其功能的全面(更深入)解釋(例如,如果存在底層集合, yield關鍵字如何工作)。

基本上,我要求的是對IEnumerable<T>的功能進行更詳細的解釋。

實施無關緊要。 所有這些(LINQ)方法都返回IEnumerable<T> ,接口成員是您可以訪問的唯一成員,這應該足以使用它們。

但是,如果您真的必須知道,可以在http://sourceof.net上找到實際的實現。

但是,對於某些方法,您將無法找到顯式類聲明,因為其中一些使用yield return ,這意味着編譯期間編譯器會生成正確的類(帶狀態機)。 例如Enumerable.Repeat就是這樣實現的:

public static IEnumerable<int> Range(int start, int count) {
    long max = ((long)start) + count - 1;
    if (count < 0 || max > Int32.MaxValue)
        throw Error.ArgumentOutOfRange("count");
    return RangeIterator(start, count); 
}

static IEnumerable<int> RangeIterator(int start, int count) {
    for (int i = 0; i < count; i++)
        yield return start + i;
}

您可以在MSDN上閱讀更多相關信息: 迭代器(C#和Visual Basic)

並非所有實現IEnumerable對象都以某種方式延遲執行。 接口的API 可以延遲執行,但它不需要它。 同樣的實現不會以任何方式推遲執行。

那么,實際項目(在示例2 * x項目中)所在的集合是什么?

空無一人。 每當請求下一個值它計算的是需求一個價值,它給調用者,然后忘記了價值。 它不會將其存儲在任何其他地方。

而且,如果我們要編寫IEnumerable<int> temp = Enumerable.Repeat(1, 10); ,什么是存儲1s的底層集合(數組,列表,其他東西)?

沒有一個。 當你要求下一個值時 ,它會立即計算每個新值,之后就不會記住它。 它只存儲足夠的信息以便能夠計算下一個值,這意味着它只需要存儲元素和剩余產生的值的數量。

雖然實際的.NET實現將使用更簡潔的方法來創建這樣的類型,但創建一個延遲執行的枚舉並不是特別困難。 這樣做即使是漫長的道路也比艱難更乏味。 您只需計算迭代器的MoveNext方法中的下一個值。 在你問的例子中, Repeat ,這很容易,因為你只需要計算是否有另一個值,而不是它是什么:

public class Repeater<T> : IEnumerator<T>
{
    private int count;
    private T element;

    public Repeater(T element, int count)
    {
        this.element = element;
        this.count = count;
    }
    public T Current { get { return element; } }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public void Dispose() { }

    public bool MoveNext()
    {
        if (count > 0)
        {
            count--;
            return true;
        }
        else
            return false;
    }

    public void Reset()
    {
        throw new NotSupportedException();
    }
}

(我省略了一個只返回此類型的新實例的IEnumerable類型,或者一個靜態Repeat方法,它創建了一個可枚舉的新實例。沒有什么特別有趣的東西可以看到。)

一個稍微有趣的例子就像Count

public class Counter : IEnumerator<int>
{
    private int remaining;

    public Counter(int start, int count)
    {
        Current = start;
        this.remaining = count;
    }
    public int Current { get; private set; }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public void Dispose() { }

    public bool MoveNext()
    {
        if (remaining > 0)
        {
            remaining--;
            Current++;
            return true;
        }
        else
            return false;
    }

    public void Reset()
    {
        throw new NotSupportedException();
    }
}

在這里,我們不僅計算我們是否有另一個值,而是每次為我們請求新值時,下一個值是什么。

那么,實際項目(在示例2 * x項目中)所在的集合是什么?

它不在任何地方。 迭代時會有“按需”生成單個項目的代碼,但不會預先計算2*x數字。 除非您調用ToListToArray ,否則它們也不會存儲在任何位置。

而且,如果我們要編寫IEnumerable temp = Enumerable.Repeat(1,10);那么存儲1s的底層集合(數組,列表,其他東西)是什么?

同樣的圖片在這里: IEnumerable的返回實現不是公共的,它按需返回其項目,而不將它們存儲在任何地方。

C#編譯器提供了一種實現IEnumerable的便捷方式,而無需為其定義類。 您所需要的只是將方法返回類型聲明為IEnumerable<T> ,並根據需要使用yield return來提供值。

暫無
暫無

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

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