//var sample= new { bottom=2, top=5,count=4};
var sample= new [] {2,3,4,5,6};
var q = from x in new int[]{0} //force desired behavior
from i in sample
from j in sample
select Math.Pow(i,j);
q.Distinct().Count().Dump();
sample = new[]{2,3,4,5};
//q = from i in Enumerable.Range(sample.bottom, sample.top-sample.bottom+1)
// from j in Enumerable.Range(sample.bottom, sample.top-sample.bottom+1)
// select checked(System.Numerics.BigInteger.Pow((BigInteger)i,j));
q.Distinct().Count().Dump();
如果from x line
不存在,或者q
变量未重置,则每次第二个答案都不正确。(此处显示已注释掉)
原始样本是一个匿名变量,但阵列也是这样做的。
var sample = new { bottom =2, top=5};
是否与此Scope of Linq Expressions defined in a loop - At issue: closing over loop variable相关?
为什么在顶部放置一个1项数组来修复闭包?
答案 0 :(得分:2)
此代码两次关闭sample
。
from x in new int[]{0}
from i in sample
from j in sample
select Math.Pow(i,j);
实际方法调用此计算结果为:
new int[] {0}
.SelectMany(x => sample, (x, i) => new {x, i})
.SelectMany(@t => sample, (@t, j) => Math.Pow(@t.i, j));
此代码一次关闭sample
。
from i in sample
from j in sample
select Math.Pow(i,j);
实际方法调用此计算结果为:
sample.SelectMany(i => sample, (i, j) => Math.Pow(i, j));
因此,如果您更改sample
,则只有i => sample
lambda会“看到”sample
的“新”值; sample.SelectMany
已经使用“旧”值运行。
这两个示例都会导致R#发出访问修改后的闭包警告,这通常是一个大红旗。考虑为每个查询保留sample
的私有副本,或者为您执行一个函数参数:
IEnumerable<int> SelectManyPow(int[] sample)
{
return from i in sample
from j in sample
select Math.Pow(i, j);
}
答案 1 :(得分:2)
理解这里发生的事情的第一步是了解如何翻译查询表达式。根据规范:
from x1 in e1
from x2 in e2
select v
被翻译成
e1.SelectMany(x1 => e2, (x1, x2) => v)
理解非常非常重要。 from x1 in e1 from x2 in e2 select v
的结果是对e1的查询,无论e1
是什么;它是已修复,期间。
现在,这是问题所在。我们来看看你的问题。
var q = from i in sample
from j in sample
select Math.Pow(i, j)
构造查询时sample
at的值是对数组创建表达式new[] { 2, 3, 4, 5, 6 }
的结果的引用。设e1
为对该数组创建表达式结果的引用。然后查询变为:
e1.SelectMany(i => sample, (i, j) => Math.Pow(i, j));
这是已修复,句号,无论您重新分配给sample
。也就是说, 想到
var sample = new[] { 2, 3, 4, 5, 6 }
var q = from i in sample
from j in sample
select Math.Pow(i, j)
作为
var sample = new[] { 2, 3, 4, 5, 6 };
var e1 = sample;
var q = e1.SelectMany(i => sample, (i, j) => Math.Pow(i, j));
现在,sample
已关闭,这就是在之后为sample
分配新值的原因,您会看到不同的查询结果。但是无论,e1
仍然是数组创建表达式new[] { 2, 3, 4, 5, 6 }
的结果。
但是,当您从查询开始
时var q = from x in new int[] { 0 }
from i in sample
from j in sample
select Math.Pow(i, j)
现在该查询被翻译为
int[] e1 = new int[] { 0 };
var q = e1.SelectMany(
x1 => sample.SelectMany(i => sample, (i, j) => Math.Pow(i, j)
);
和现在 sample
被捕获为i
和 j
的序列,但e1
已修复