4
//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};

它是否与循环中定义的 Linq 表达式范围有关 - 有问题:关闭循环变量

为什么将 1 项数组放在顶部修复了闭包?

4

2 回答 2

2

这段代码关闭了sample两次。

from x in new int[]{0}
from i in sample
from j in sample
select Math.Pow(i,j);

调用 this 的实际方法是:

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);

调用 this 的实际方法是:

sample.SelectMany(i => sample, (i, j) => Math.Pow(i, j));

所以如果你改变sample了,只有i => samplelambda 会“看到” ; 的“新”值samplesample.SelectMany已经以“旧”值运行。

这两个示例都会导致 R# 发出Access to modified closure警告,这通常是一个很大的危险信号。sample考虑为您的每个查询保留一个私有副本,或者让一个函数参数为您执行此操作:

IEnumerable<int> SelectManyPow(int[] sample)
{
    return from i in sample
           from j in sample
           select Math.Pow(i, j);
}
于 2013-08-04T01:37:05.727 回答
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)
);

and now sample被捕获为两者的序列i j但是e1是固定的。

于 2013-08-04T01:45:38.027 回答