列舉可列舉
IEnumerable <T>介面是所有通用列舉器的基本介面,是理解 LINQ 的典型部分。它的核心是代表序列。
此底層介面由所有泛型集合繼承,例如 Collection <T> , Array , List <T> , Dictionary <TKey,TValue> Class 和 HashSet <T> 。
除了表示序列之外,任何從 IEnumerable <T>繼承的類都必須提供 IEnumerator <T>。列舉器公開了可列舉的迭代器,這兩個相互關聯的介面和思想是列舉可列舉的源頭。
列舉可列舉的是一個重要的短語。可列舉只是一個如何迭代的結構,它不包含任何具體化的物件。例如,在排序時,可列舉可以保持欄位的條件進行排序,但是使用 .OrderBy()
本身將返回 IEnumerable <T>,它只知道如何排序。使用將實現物件的呼叫(如迭代集合)稱為列舉(例如 .ToList()
)。列舉過程將使用可列舉的定義如何在系列中移動並返回相關物件(按順序,過濾,投影等)。
只有列舉了列舉後,它才會導致物件的具體化,這就像時間複雜度 (與系列大小相關應該花多長時間)和空間複雜度(它應該使用多少與系列大小相關的空間)等指標一樣被測量。
建立自己的繼承自 IEnumerable <T>的類可能有點複雜,具體取決於需要列舉的基礎系列。通常,最好使用現有的通用集合之一。也就是說,也可以從 IEnumerable <T>介面繼承,而不必將已定義的陣列作為底層結構。
例如,使用 Fibonacci 系列作為基礎序列。請注意,對 Where
的呼叫只是構建了一個 IEnumerable
,直到列舉的列舉被認為是任何值都已實現的列舉。
void Main()
{
Fibonacci Fibo = new Fibonacci();
IEnumerable<long> quadrillionplus = Fibo.Where(i => i > 1000000000000);
Console.WriteLine("Enumerable built");
Console.WriteLine(quadrillionplus.Take(2).Sum());
Console.WriteLine(quadrillionplus.Skip(2).First());
IEnumerable<long> fibMod612 = Fibo.OrderBy(i => i % 612);
Console.WriteLine("Enumerable built");
Console.WriteLine(fibMod612.First());//smallest divisible by 612
}
public class Fibonacci : IEnumerable<long>
{
private int max = 90;
//Enumerator called typically from foreach
public IEnumerator GetEnumerator() {
long n0 = 1;
long n1 = 1;
Console.WriteLine("Enumerating the Enumerable");
for(int i = 0; i < max; i++){
yield return n0+n1;
n1 += n0;
n0 = n1-n0;
}
}
//Enumerable called typically from linq
IEnumerator<long> IEnumerable<long>.GetEnumerator() {
long n0 = 1;
long n1 = 1;
Console.WriteLine("Enumerating the Enumerable");
for(int i = 0; i < max; i++){
yield return n0+n1;
n1 += n0;
n0 = n1-n0;
}
}
}
輸出
Enumerable built
Enumerating the Enumerable
4052739537881
Enumerating the Enumerable
4052739537881
Enumerable built
Enumerating the Enumerable
14930352
第二組(fibMod612)中的強度即使我們呼叫了整個 Fibonacci 數的整數,因為使用 .First()
只取一個值,時間複雜度為 O(n)
,因為只需要 1 個值在排序演算法執行期間進行了比較。這是因為我們的列舉器只詢問了 1 個值,因此不需要實現整個可列舉。如果我們使用 .Take(5)
代替 .First()
,則列舉器會要求 5 個值,並且最多需要實現 5 個值。與需要訂購整套然後取前 5 個值相比,原理節省了大量的執行時間和空間。