Правильное использование блоков итераторов - PullRequest
8 голосов
/ 10 февраля 2012

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

14 голосов
/ 10 февраля 2012

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

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

3 голосов
/ 10 февраля 2012

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

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

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

3 голосов
/ 10 февраля 2012

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

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

3 голосов
/ 10 февраля 2012

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

0 голосов
/ 10 февраля 2012

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

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

foreach(var ent in ents)
{
    if(someCondition)
    yield return new MyBusinessEnt {
        Id = ent.Id,
        Code = ent.Code
    };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...