2010-03-10 1 views
38

Рассмотрите этот бит запутанного кода. Цель состоит в том, чтобы создать новый объект «на лету» с помощью анонимного конструктора и yield return. Цель состоит в том, чтобы избежать необходимости поддерживать местную коллекцию, просто чтобы просто return.C#: возврат доходности внутри foreach терпит неудачу - тело не может быть блоком итератора

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?

ответ

49

Вы можете использовать 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 
    }); 
16

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

public static IEnumerable<DesktopComputer> BuildComputerAssets() 
8

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

Заявление выхода может появляться только внутри блока итератора

Iterators определен как

Возвращаемый тип итератора должен быть IEnumerable, IEnumerator, IEnume годовой <T>, или IEnumerator <T>.

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

2

Вы также можете реализовать ту же функциональность, используя запрос 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).

Смежные вопросы