Правильное использование блоков итератора

Я был рефакторинг кода раньше и я наткнулся на реализацию итератора блока я тоже не был уверен. На уровне интеграции системы, где клиент вызывает внешний API для некоторых данных, у меня есть набор переводчиков, которые берут данные, возвращенные из API, и переводят их в коллекции бизнес-объектов, используемых на уровне логики. Общий класс переводчика будет выглядеть так:

// translate a collection of entities coming back from an extrernal source into business entities
public static IEnumerable<MyBusinessEnt> Translate(IEnumerable<My3rdPartyEnt> ents) {

    // for each 3rd party ent, create business ent and return collection
    return from ent in ents
           select new MyBusinessEnt {
               Id = ent.Id,
               Code = ent.Code
           };
}

сегодня я наткнулся на следующий код. Снова, это класс-переводчик, его цель-перевести коллекцию в параметре в тип возврата метода. Однако на этот раз это блок итератора:

// same implementation of a translator but as an iterator block
public static IEnumerable<MyBusinessEnt> Translate(IEnumerable<My3rdPartyEnt> ents) {
    foreach(var ent in ents)
    {
        yield return new MyBusinessEnt {
            Id = ent.Id,
            Code = ent.Code
        };
    }
}

мой вопрос: является ли это допустимым использованием блока итератора? Я не вижу пользы в создании класса переводчика таким образом. Это может привести к неожиданному поведению?

5 ответов


ваши два образца делают почти то же самое. Версия запроса будет переписана в вызов Select, а Select записывается точно так же, как и ваш второй пример; он повторяет каждый элемент в исходной коллекции и возвращает преобразованный элемент.

это совершенно допустимое использование блока итератора, хотя, конечно, это больше не необходимые написать собственный итератор блоков, как это, потому что вы можете просто использовать Select.


Да, это правильно. The foreach имеет преимущество быть отлаживаемым, поэтому я предпочитаю этот дизайн.


первый пример не является итератором. Он просто создает и возвращает IEnumerable<MyBusinessEnt>.

второй-это итератор, и я не вижу ничего плохого. Каждый раз, когда вызывающий объект повторяет возвращаемое значение этого метода,yield возвращает новый элемент.


Да, это отлично работает, и результат очень похож.

создает объект, который способен возвращать результат. Оба полагаются на источник, перечисляемый, чтобы оставаться неповрежденным до тех пор, пока результат не будет завершен (или сокращен). Оба используют отложенное выполнение, т. е. объекты создаются по одному во время итерации результата.

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


на большая разница на , когда каждый код работает. первый задерживается до тех пор, пока возвращаемое значение не будет повторено, а второй запускается немедленно. Я имею в виду, что цикл заставляет итерацию работать. Тот факт, что класс предоставляет IEnumerable<T> и в этом случае задержка-это другое дело.

это не дает никакой выгоды по сравнению с простой Select. yieldреальная власть, когда есть условное участвует:

foreach(var ent in ents)
{
    if(someCondition)
    yield return new MyBusinessEnt {
        Id = ent.Id,
        Code = ent.Code
    };
}