Looking for c# Keywords? Try Ask4Keywords

C# Language Перечисление Перечислимого


пример

Интерфейс 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>, не имея определенного массива в качестве базовой структуры.

Например, использование серии Фибоначчи в качестве базовой последовательности. Обратите внимание, что вызов 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) заключается в том, что, хотя мы сделали вызов упорядочить весь наш набор чисел Фибоначчи, поскольку только одно значение было принято с использованием .First() сложность времени была O (n), поскольку только 1 значение необходимо было сравнить при выполнении алгоритма упорядочения. Это связано с тем, что наш счетчик запрашивал только 1 значение, поэтому весь перечислимый материал не должен был быть реализован. Если бы мы использовали .Take(5) вместо .First() перечислитель запросил бы 5 значений, и не более 5 значений должны были быть реализованы. По сравнению с необходимостью заказа всего набора, а затем принимать первые 5 значений, принцип сохраняет много времени и пространства выполнения.