yield和List.AsEnumerable之间的差异

kun*_*jee 22 c# c#-4.0

到目前为止,收益是我很难理解的.但现在我抓住了它.现在,在一个项目中,如果我返回List,Microsoft代码分析将发出警告.所以,通常我会做所有必要的逻辑部分并将列表作为IEnumerable返回.我想知道两者之间的区别.意味着我是否正在收益率或其他方式.

这是我展示的一个非常简单的例子,通常代码有点复杂.

private static IEnumerable<int> getIntFromList(List<int> inputList)
{
    var outputlist = new List<int>();
    foreach (var i in inputList)
    {
        if (i %2 ==0)
        {
            outputlist.Add(i);
        }
    }

    return outputlist.AsEnumerable();
}

private static IEnumerable<int> getIntFromYeild(List<int> inputList)
{
    foreach (var i in inputList)
    {
        if (i%2 == 0)
        {
            yield return i;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我能看到的一个重要好处是线路更少.但还有其他好处吗?我应该更改和更新返回IEnumearble以使用yield而不是List的函数吗?什么是最好的方式或更好的做事方式?

在这里,我可以在List上使用简单的lambda表达式,但通常情况并非如此,这个例子专门用于理解编码的最佳方法.

Jon*_*eet 46

您的第一个示例仍然是热切地完成所有工作并在内存中构建列表.事实上,呼吁AsEnumerable()是毫无意义的 - 你不妨使用:

return outputlist;
Run Code Online (Sandbox Code Playgroud)

你的第二个例子是懒惰的 - 当客户端从中提取数据时,它只能完成所需的工作量.

显示差异的最简单方法可能是Console.WriteLineif (i % 2 == 0)语句中调用:

Console.WriteLine("Got a value to return: " + i);
Run Code Online (Sandbox Code Playgroud)

然后,如果你Console.WriteLine调用客户端代码,例如

foreach (int value in getIntFromList(list))
{
    Console.WriteLine("Received value: " + value);
}
Run Code Online (Sandbox Code Playgroud)

...你会看到第一个代码,你首先看到所有"有一个值"的行,然后是所有"已接收的值"行.使用迭代器块,您将看到它们交错.

现在假设您的代码实际上做了一些昂贵的事情,而且您的列表很长,并且客户端只需要前3个值...使用您的第一个代码,您将进行大量无关的工作.使用懒惰的方法,您只需要以"及时"的方式完成所需的工作.第二种方法也不需要将所有结果缓冲到内存中 - 再次,如果输入列表非常大,您最终也会得到一个大的输出列表,即使您只想使用单个值一时间

  • 你希望通过文本向他人解释事物的能力是我希望有朝一日实现的. (15认同)

Mar*_*ell 17

关键yield return是它没有缓冲 ; 迭代器块是一个状态机,它在迭代数据时恢复.这使得它非常适合非常大的数据源(甚至是无限列表),因为您可以避免使用大量的内存列表.

以下是一个完美定义的迭代器块,可以成功迭代:

Random rand = new Random();
while(true) yield return rand.Next();
Run Code Online (Sandbox Code Playgroud)

我们可以做以下事情:

for(int i in TheAbove().Take(20))
    Console.WriteLine(i);
Run Code Online (Sandbox Code Playgroud)

虽然很明显,任何迭代到底的东西(比如Count()等)都会永远运行而不会结束 - 这不是一个好主意.

在您的示例中,代码可能过于复杂.该List<int>版本可能只是:

return new List<int>(inputList);
Run Code Online (Sandbox Code Playgroud)

yield return还挺取决于你想要做什么:在最简单的,它可能只是:

foreach(var item in inputList) yield return item;
Run Code Online (Sandbox Code Playgroud)

虽然显然仍然会查​​看源数据:更改inputList可能会破坏迭代器.如果你认为"那很好",那么坦率地说你也可能只是:

return inputList;
Run Code Online (Sandbox Code Playgroud)

如果这不好,在这种情况下,迭代器块有点矫枉过正,并且:

return new List<int>(inputList);
Run Code Online (Sandbox Code Playgroud)

应该足够了.

为了完整性:AsEnumerable只返回原始源,输入类型 ; 它是:

return inputList;
Run Code Online (Sandbox Code Playgroud)

版.这有一个重要的考虑因素,因为如果这是一个问题,它不会保护您的列表.所以,如果你在想:

return someList.AsEnumerable(); // so they can only iterate it, not Add
Run Code Online (Sandbox Code Playgroud)

然后这将无法奏效; 一个邪恶的来电者仍然可以这样做:

var list = (IList<int>) theAbove;
int mwahaahahaha = 42;
list.Add(mwahaahahaha);
Run Code Online (Sandbox Code Playgroud)