C# 来自Eric Lippert的博客:“不要关闭循环变量”
可能的重复项: 从输入开始: var s=序列;看起来像是禁区,为什么不是?直接使用序列会出现什么问题C# 来自Eric Lippert的博客:“不要关闭循环变量”,c#,linq,closures,C#,Linq,Closures,可能的重复项: 从输入开始: var s=序列;看起来像是禁区,为什么不是?直接使用序列会出现什么问题 而且,更主观地说:这在多大程度上被认为是C行为中的一个缺点?这是一个微妙的范围问题,与闭包和延迟执行的工作方式有关 如果不使用局部变量,而是直接使用序列,则结果IEnumarable将绑定到变量序列而不是序列的值,并且在执行查询时,变量序列包含序列的最后一个值 如果像Eric的示例中那样声明另一个局部变量,则范围仅限于每个循环迭代。因此,即使推迟执行,也将按照预期进行评估。Eric本人的几篇
而且,更主观地说:这在多大程度上被认为是C行为中的一个缺点?这是一个微妙的范围问题,与闭包和延迟执行的工作方式有关 如果不使用局部变量,而是直接使用序列,则结果IEnumarable将绑定到变量序列而不是序列的值,并且在执行查询时,变量序列包含序列的最后一个值
如果像Eric的示例中那样声明另一个局部变量,则范围仅限于每个循环迭代。因此,即使推迟执行,也将按照预期进行评估。Eric本人的几篇相关文章,以及评论中的一些有趣的讨论:
该博客帖子上的评论之一: 然而,在您的第一个应用程序中有一个bug CartesianProduct方法的版本 :您正在接近环路 变量,因此,由于延迟 执行,它使笛卡尔 最后一个序列与的乘积 它本身您需要添加一个临时 foreach循环中的局部变量 让它在第二个版本中工作 不过效果不错
这里使用的LINQ查询导致s的值在最初定义的范围(即CartesianProduct方法)之外可用。这就是所谓的a。由于执行延迟,当LINQ查询实际被评估时(假设它最终被评估),封闭方法将完成,s变量将“超出范围”,至少根据传统的范围规则是这样。尽管如此,在这种情况下,引用s 闭包在传统的函数式编程语言中非常方便且表现良好,在这种语言中,事物自然是不可变的。事实上,C语言首先是一种命令式编程语言,默认情况下变量是可变的,这是导致这种奇怪的解决方法的基础
通过在循环范围内创建中间变量,可以有效地指示编译器为LINQ查询的每次迭代分配一个单独的非共享变量。否则,每次迭代都将共享同一个变量实例,这显然也是相同的值…可能不是您想要的。这已经被问过1000次了,我不知道这一点,很高兴已经有一百万个线程了。
static IEnumerable<IEnumerable<T>>
CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
// base case:
IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() };
foreach(var sequence in sequences)
{
var s = sequence; // don't close over the loop variable
// recursive case: use SelectMany to build the new product out of the old one
result =
from seq in result
from item in s
select seq.Concat(new[] {item});
}
return result;
}