考虑以下C#程序:
using System;
using System.Linq;
using System.Collections.Generic;
public class Test
{
static IEnumerable<Action> Get()
{
for (int i = 0; i < 2; i++)
{
int capture = i;
yield return () => Console.WriteLine(capture.ToString());
}
}
public static void Main(string[] args)
{
foreach (var a in Get()) a();
foreach (var a in Get().ToList()) a();
}
}
在Mono编译器下执行时(例如Mono 2.10.2.0 - 粘贴到here),它会写入以下输出:
0
1
1
1
这对我来说似乎完全不合逻辑。当直接迭代yield函数时,for循环的范围是“正确地”(根据我的理解)使用的。但是当我首先将结果存储在列表中时,范围始终是最后一个操作?!
我可以假设这是Mono编译器中的一个错误,还是我遇到了C#的lambda和yield-stuff的神秘角落?
顺便说一句:当使用Visual Studio编译器(以及MS.NET或单声道执行)时,结果是预期的0 1 0 1
答案 0 :(得分:1)
我会告诉你原因0 1 1 1
:
foreach (var a in Get()) a();
在这里你进入Get并开始迭代:
i = 0 => return Console.WriteLine(i);
yield
返回函数并执行函数,向屏幕打印0,然后返回Get()
方法并继续。
i = 1 => return Console.WriteLine(i);
yield
返回函数并执行函数,将1打印到屏幕,然后返回Get()
方法并继续(仅发现它必须停止)。
但是现在,你没有迭代每个项目,你正在构建一个列表,然后迭代该列表。
foreach (var a in Get().ToList()) a();
你正在做的不像上面那样,Get().ToList()
返回一个列表或数组(不确定是哪一个)。所以现在发生这种情况:
i = 0 => return Console.WriteLine(i);
在你Main()
函数中,你在内存中得到以下内容:
var i = 0;
var list = new List
{
Console.WriteLine(i)
}
您将返回Get()
函数:
i = 1 => return Console.WriteLine(i);
返回Main()
var i = 1;
var list = new List
{
Console.WriteLine(i),
Console.WriteLine(i)
}
然后呢
foreach (var a in list) a();
将打印出1 1
似乎忽略了你确保在返回函数之前封装了值。
答案 1 :(得分:0)
@Armaron - .ToList()扩展返回类型为T的List,因为命名约定意味着ToArray()返回T [],但我认为你的回答是正确的。
这听起来像编译器的一个问题。我同意Servy认为这可能是一个错误,但是,您是否尝试过以下操作?
public class Test
{
private static int capture = 0;
static IEnumerable<Action> Get()
{
for (int i = 0; i < 2; i++)
{
capture++;
yield return () => Console.WriteLine(capture.ToString());
}
}
}
此外,您可能想要尝试静态方法,也许这将执行更准确的转换,因为您的函数是静态的。
List<T> list = Enumerable.ToList(Get());
当调用ToList()时,似乎它没有为每个值执行单次迭代,而是:
return new List<T>(Get());
除非您需要向List对象添加/删除其他操作,否则代码中每个代码的第二个对我来说在实现中没有意义,除非您需要执行其他操作,否则它将是必要的或有益的。第一个是完美的意义,因为你所做的只是遍历对象并执行相关的操作。我的理解是,在转换期间通过执行整个迭代来计算静态IEnumerbale对象范围内的整数,并且该操作由于范围而将int保留为静态int。另外,请记住,IEnumerable只是一个由List实现的接口,它实现了IList,并且可能包含内置转换的逻辑。
话虽如此,我有兴趣看到/听到您的发现,因为这是一篇有趣的帖子。我肯定会提出这个问题。如果我说的任何内容需要澄清或者有些事情是假的,请提出问题,尽管我对使用IEnumerable的yield关键字有信心,但这是一个独特的问题。