如果lambda内的语句用于整个查询

时间:2011-11-29 22:23:07

标签: c# lambda

这个陈述可以用1个lambda表达式吗?

if (filter.SubFormId.HasValue)
{
    query = query.Where(c => c.SubFormId == filter.SubFormId);
}

3 个答案:

答案 0 :(得分:9)

对此类查询非常小心。 where子句绑定到变量过滤器,而不是当前值

原始代码:

var query = from form in forms select form;
var filter = new Filter();
filter.SubFormId = 123;
if (filter.SubFormId.HasValue)
{
    query = query.Where(c => c.SubFormId == filter.SubFormId);
}
filter = null;
foreach(var matchingForm in query)
{ ... }

该代码崩溃了。 查询使用过滤器的当前值,而不是查询时的值。

Jared的版本并没有更好:

var query = from form in forms select form;
var filter = new Filter();
filter.SubFormId = 123;
query = query.Where(c => 
  !filter.SubFormId.HasValue || c.SubFormId == filter.SubFormId);
filter = null;
foreach(var matchingForm in query)
{ ... }

那也崩溃了。

如果过滤器以其他方式发生变异,您的版本和Jared的版本也会有不同的语义:

原始代码:

var query = from form in forms select form;
var filter = new Filter();
filter.SubFormId = null;
if (filter.SubFormId.HasValue)
{
    query = query.Where(c => c.SubFormId == filter.SubFormId);
}
filter.SubFormId = 123;
foreach(var matchingForm in query)
{ ... }

匹配每个表单。你从未添加过Where子句。

Jared的版本做了不同的事情:

var query = from form in forms select form;
var filter = new Filter();
filter.SubFormId = null;
query = query.Where(c => 
  !filter.SubFormId.HasValue || c.SubFormId == filter.SubFormId);
filter.SubFormId = 123;
foreach(var matchingForm in query)
{ ... }

仅匹配subformId等于123的表单。 Jared的版本与过滤子表单Id的当前值匹配(如果有的话),无论是否在构建查询时都存在。 / em>的

请记住,查询是表示查询的对象。执行查询时,将从头开始重新执行查询,并根据查询关闭的变量的当前值评估查询中的子句,而不是根据值的快照对其进行评估。创建查询时存在的变量。查询具有实时,最新的变量视图。

  

所以我想说我也返回这个查询,它允许在方法之外评估查询。由于过滤器超出范围,这会崩溃吗?

简短回答:

没有。

更长的答案:

这是这个问题的重复:

Action/Lambda Expression Memory Management Question

详情请参阅我的回答。

答案很长:

您将范围与生命周期混淆 - 这是一个常见错误。范围纯粹是编译时的概念;局部变量的范围是程序文本的区域,其中该变量可以通过其名称引用。变量的生命周期纯粹是运行时概念;局部变量的生存期是垃圾收集器知道存储有效的时间段。

范围和寿命经常相关;虽然控制在逻辑上是在本地范围内的程序文本的一部分中,但是已知它是活着的。查询,lambda,迭代器块和异步块中使用的本地生命周期延长,因此即使控制离开本地范围,本地仍然存在。本地保持活着至少直到查询,lambda等已经死亡。

不幸的是,在某些情况下,当地的居住时间更长;请参阅上面的链接问题以获取示例。有关此问题的扩展讨论,请参阅此问题:

C# Action, Closure, and Garbage Collection

答案 1 :(得分:3)

尝试以下

query = query.Where(c => 
  !filter.SubFormId.HasValue || c.SubFormId == filter.SubFormId);

答案 2 :(得分:1)

如果条件不是 true,您没有指定query的值应该是什么

但这可能接近你的预期

query = query.Where(c => 
      filter.SubFormId.HasValue && c.SubFormId == filter.SubFormId);

如果SubFormId null

,则导致枚举

query = query.Where(c => 
      filter.SubFormId.HasValue? c.SubFormId == filter.SubFormId : true);

导致未经过滤的列表


编辑我想继续Mark的评论,这很糟糕

query = filter.SubFormId.HasValue
     ? query.Where(c => c.SubFormId == filter.SubFormId) 
     : query;

这几乎是你的原始代码。您的原始代码将比强制的Linq风格版本更有效。