C#:foreach中的yield return失败 - body不能成为迭代器块

时间:2010-03-10 01:12:27

标签: c# iterator yield

考虑一下混淆代码。目的是通过匿名构造函数和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

4 个答案:

答案 0 :(得分:52)

您只能在返回yield returnIEnumerable而不是IEnumerator的函数中使用List<T>

您需要更改功能以返回IEnumerable<DesktopComputer>

或者,您可以重写函数以使用List<T>.ConvertAll

return GetComputerIdTags().ConvertAll(pcTag => 
    new DesktopComputer() {
        AssetTag    = pcTag,
        Description = "PC " + pcTag,
        AcquireDate = DateTime.Now
    });

答案 1 :(得分:17)

您的方法签名错误。它应该是:

public static IEnumerable<DesktopComputer> BuildComputerAssets()

答案 2 :(得分:8)

yield仅适用于迭代器类型:

  

yield语句只能出现在迭代器块

Iterators定义为

  

迭代器的返回类型必须是IEnumerable,IEnumerator,IEnumerable&lt; T&gt;或IEnumerator&lt; T&gt;。

IList和IList&lt; T&gt;确实实现IEnumerable / IEnumerable&lt; T&gt;,但枚举器的每个调用者都需要上述四种类型中的一种,而不是其他类型。

答案 3 :(得分: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)。