我在下面的程序中注意到了奇怪的Linq select
行为:
IEnumerable<string> s = new List<string>() { "1", "2", "3" };
var i = s.Select(url =>
{
Console.WriteLine(url);
url = string.Format("--{0}--",url);
return url;
}
);
Console.WriteLine("done with selector");
foreach (string f in i)
{
Console.WriteLine("f is {0}", f);
}
输出是:
1
f is --1--
2
f is --2--
3
f is --3--
我期待输出为:
1
2
3
f is --1--
f is --2--
f is --3--
如何解释这种奇怪的行为?它是某种代码优化吗?
答案 0 :(得分:3)
您的代码完美地说明了延迟执行在LINQ中的工作方式:
"done with selector"
,因为Select
“记住”计算序列的信息,而不是实际计算它Select
和WriteLine
内的lambda的行是交错的,因为序列是在你继续枚举时生成的如果您在ToList
之后添加Select
,您的代码将“急切地”生成序列,从而产生您期望的输出(demo)。
答案 1 :(得分:1)
试试这个。
IEnumerable<string> s = new List<string>() { "1", "2", "3" };
var i = s.Select(url =>
{
Console.WriteLine(url);
url = string.Format("--{0}--",url);
return url;
}
).ToList();
Console.WriteLine("done with selector");
foreach (string f in i)
{
Console.WriteLine("f is {0}", f);
}
由于延迟执行,第一行中的lambda表达式只会在第二行中迭代结果时执行。
此行为完全正确。
Here是一个非常好的解释。
答案 2 :(得分:0)
您期望的是查询执行并填充您定义查询的变量i
。
这种行为可以通过在选择之后强制转换.ToList()来强制(并且通常在编写得不太好的代码中不必要):
IEnumerable<string> s = new List<string>() { "1", "2", "3" };
var i = s.Select(url =>
{
Console.WriteLine(url);
url = string.Format("--{0}--",url);
return url;
}
).ToList();
Console.WriteLine("done with selector");
foreach (string f in i)
{
Console.WriteLine("f is {0}", f);
}
但很少需要。
此行为类似于在c#中使用yield和yield return关键字时所遇到的行为。你可能值得一点研究它们。