43

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

4 回答 4

54

您只能在返回 an或 an而不是 ayield return的函数中使用。IEnumerableIEnumeratorList<T>

您需要更改函数以返回IEnumerable<DesktopComputer>.

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

return GetComputerIdTags().ConvertAll(pcTag => 
    new DesktopComputer() {
        AssetTag    = pcTag,
        Description = "PC " + pcTag,
        AcquireDate = DateTime.Now
    });
于 2010-03-10T01:13:53.213 回答
16

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

public static IEnumerable<DesktopComputer> BuildComputerAssets()
于 2010-03-10T01:14:32.463 回答
8

yield仅适用于迭代器类型:

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

迭代器定义为

迭代器的返回类型必须是 IEnumerable、IEnumerator、IEnumerable<T> 或 IEnumerator<T>。

IList 和 IList<T> 确实实现了 IEnumerable/IEnumerable<T>,但是每个枚举器的调用者都期望上述四种类型中的一种,而不是其他类型。

于 2010-03-10T01:21:59.573 回答
1

您还可以使用 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)。

于 2010-03-10T02:35:54.603 回答