С#: возврат результата в пределах foreach не удался - тело не может быть блоком итератора
Рассмотрим этот бит обфускации кода. Цель состоит в том, чтобы создать новый объект "на лету" через анонимный конструктор и yield return
его. Цель состоит в том, чтобы избежать необходимости поддерживать локальную коллекцию только для просто 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
?
Ответы
Ответ 1
Вы можете использовать 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
});
Ответ 2
Ваша подпись метода неверна. Это должно быть:
public static IEnumerable<DesktopComputer> BuildComputerAssets()
Ответ 3
yield работает только с типами Iterator:
Оператор yield может отображаться только внутри блока итератора
Итераторы определяются как
Возвращаемый тип итератора должен быть IEnumerable, IEnumerator, IEnumerable <T> , или IEnumerator <T> .
IList и IList <T> do реализуют IEnumerable/IEnumerable <T> , но каждый вызывающий объект для перечислителя ожидает один из четырех типов выше и не более того.
Ответ 4
Вы также можете реализовать те же функции, используя запрос LINQ (в С# 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
).