Помогите мне понять фрагмент кода в С#

Я читаю этот блог: Паттерны труб и фильтров

Я смущен этим фрагментом кода:

public class Pipeline<T>
{
    private readonly List<IOperation<T>> operations = new List<IOperation<T>>();

    public Pipeline<T> Register(IOperation<T> operation)
    {
        operations.Add(operation);
        return this;
    }

    public void Execute()
    {
        IEnumerable<T> current = new List<T>();
        foreach (IOperation<T> operation in operations)
        {
            current = operation.Execute(current);
        }
        IEnumerator<T> enumerator = current.GetEnumerator();
        while (enumerator.MoveNext());
    }
}

Какова цель этого утверждения: while (enumerator.MoveNext());? кажется, этот код является noop.

Ответ 1

Сначала рассмотрим это:

IEnumerable<T> current = new List<T>();
foreach (IOperation<T> operation in operations)
{
    current = operation.Execute(current);
}

Этот код, как представляется, создает вложенные перечисления, каждый из которых принимает элементы из предыдущего, применяет к ним некоторую операцию и передает результат в следующий. Но он только строит перечислимые числа. На самом деле ничего не происходит. Он просто готов к работе, сохранен в переменной current. Существует множество способов реализовать IOperation.Execute, но это может быть что-то вроде этого.

IEnumerable<T> Execute(IEnumerable<T> ts)
{
    foreach (T t in ts)
        yield return this.operation(t); // Perform some operation on t.
}

Другой вариант, предложенный в статье, - это сортировка:

IEnumerable<T> Execute(IEnumerable<T> ts)
{
    // Thank-you LINQ!
    // This was 10 lines of non-LINQ code in the original article.
    return ts.OrderBy(t => t.Foo);
}

Теперь посмотрим на это:

IEnumerator<T> enumerator = current.GetEnumerator();
while (enumerator.MoveNext());

Это фактически приводит к выполнению цепочки операций. Когда элементы запрашиваются из перечисления, они заставляют элементы из исходного перечислимого пройти через цепочку IOperations, каждая из которых выполняет некоторую операцию над ними. Конечный результат отбрасывается, поэтому интересен только побочный эффект операции - например, запись на консоль или запись в файл. Это был бы более простой способ написать две последние строки:

foreach (T t in current) {}

Еще одна вещь, которую следует соблюдать, состоит в том, что начальный список, который запускает процесс, представляет собой пустой список, поэтому для того, чтобы это имело смысл, некоторые экземпляры T должны быть созданы внутри первой операции. В этой статье это делается путем запроса пользователя для ввода с консоли.

Ответ 2

В этом случае while (enumerator.MoveNext()); просто оценивает все элементы, возвращаемые окончательным IOperation<T>. Это выглядит немного запутанным, но пустой List<T> создается только для того, чтобы передать значение первому IOperation<T>.

Во многих коллекциях это ничего не сделает, как вы предлагаете, но учитывая, что мы говорим о шаблоне труб и фильтров, вполне вероятно, что конечное значение - это своего рода итератор, который вызовет выполнение кода. Это может быть что-то вроде этого, например (предполагая, что это целое число):

public class WriteToConsoleOperation : IOperation<int>
{
    public IEnumerable<int> Execute(IEnumerable<int> ints)
    {
        foreach (var i in ints)
        {
            Console.WriteLine(i);
            yield return i;
        }
     }
}

Таким образом, вызов MoveNext() для каждого элемента в IEnumerator<int>, возвращаемого этим итератором, вернет каждое из значений (которые игнорируются в цикле while), но также выводит каждое из значений на консоль.

Это имеет смысл?

Ответ 3

while (enumerator.MoveNext());

Внутри текущего блока кода нет никакого влияния (он перемещается по всем элементам перечисления). Отображаемый код не действует на текущий элемент в перечислении. Что может случиться, так это то, что метод MoveNext() перемещается к следующему элементу и делает что-то с объектами в коллекции (обновляет внутреннее значение, вытаскивает следующий из базы данных и т.д.). Поскольку тип List<T>, это, вероятно, не так, но в других случаях это может быть.