C #: возврат в пределах foreach не выполняется - тело не может быть блоком итератора - PullRequest
41 голосов
/ 10 марта 2010

Рассмотрим этот бит запутанного кода. Намерение состоит в том, чтобы создать новый объект на лету через анонимный конструктор и yield return it. Цель состоит в том, чтобы избежать необходимости поддерживать локальную коллекцию просто для return ее.

public static List<DesktopComputer> BuildComputerAssets()
{           
    List<string> idTags = GetComputerIdTags();

    foreach (var pcTag in idTags)
    {
        yield return new DesktopComputer() {AssetTag= pcTag
                                          , Description = "PC " + pcTag
                                          , AcquireDate = DateTime.Now
                                           };
    }            
}

К сожалению, этот бит кода выдает исключение:

Ошибка 28 Тело «Foo.BuildComputerAssets ()» не может быть блоком итератора, поскольку «System.Collections.Generic.List» не является типом интерфейса итератора

Вопросы

  • Что означает это сообщение об ошибке?
  • Как мне избежать этой ошибки и правильно использовать yield return?

Ответы [ 4 ]

52 голосов
/ 10 марта 2010

Вы можете использовать yield return только в функции, которая возвращает IEnumerable или IEnumerator, а не List<T>.

Вам нужно изменить функцию, чтобы она возвращала IEnumerable<DesktopComputer>.

Кроме того, вы можете переписать функцию для использования List<T>.ConvertAll:

return GetComputerIdTags().ConvertAll(pcTag => 
    new DesktopComputer() {
        AssetTag    = pcTag,
        Description = "PC " + pcTag,
        AcquireDate = DateTime.Now
    });
17 голосов
/ 10 марта 2010

Ваш метод подписи неверен. Должно быть:

public static IEnumerable<DesktopComputer> BuildComputerAssets()
8 голосов
/ 10 марта 2010

yield работает только на типах итераторов:

Оператор yield может появляться только внутри блока итератора

Итераторы определены как

Тип возвращаемого значения итератора должен быть IEnumerable, IEnumerator, IEnumerable или IEnumerator .

IList и IList действительно реализуют IEnumerable / IEnumerable , но каждый вызывающий в перечислителе ожидает один из четырех указанных выше типов и ни один другой.

2 голосов
/ 10 марта 2010

Вы также можете реализовать ту же функциональность, используя запрос LINQ (в C # 3.0+). Это менее эффективно, чем использование метода ConvertAll, но оно носит более общий характер. Позже вам также может понадобиться использовать другие функции LINQ, такие как фильтрация:

return (from pcTag in GetComputerIdTags()
        select new DesktopComputer() { 
          AssetTag    = pcTag, 
          Description = "PC " + pcTag, 
          AcquireDate = DateTime.Now 
        }).ToList();

Метод ToList преобразует результат из IEnumerable<T> в List<T>. Лично мне не нравится ConvertAll, потому что он делает то же самое, что и LINQ. Но поскольку он был добавлен ранее, его нельзя использовать с LINQ (его следовало назвать Select).

...