[英]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
數字。 除非您調用ToList
或ToArray
,否則它們也不會存儲在任何位置。
而且,如果我們要編寫IEnumerable temp = Enumerable.Repeat(1,10);那么存儲1s的底層集合(數組,列表,其他東西)是什么?
同樣的圖片在這里: IEnumerable
的返回實現不是公共的,它按需返回其項目,而不將它們存儲在任何地方。
C#編譯器提供了一種實現IEnumerable
的便捷方式,而無需為其定義類。 您所需要的只是將方法返回類型聲明為IEnumerable<T>
,並根據需要使用yield return
來提供值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.