lambda中的If语句用于整个查询

本文关键字:查询 用于 语句 中的 If lambda | 更新日期: 2023-09-27 18:16:03

这个语句可以写成1个lambda表达式吗?

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

lambda中的If语句用于整个查询

对于这种查询要非常小心。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)
{ ... }

代码崩溃。查询使用filter的当前值,而不是查询时的值。

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的当前值(如果有的话),而不管在构建查询时是否有。

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

那么假设我也返回这个查询,这允许在方法之外对查询进行计算。由于过滤器将超出作用域,这会导致崩溃吗?

简短回答:

较长的回答:

这是这个问题的重复:

Action/Lambda表达式内存管理问题

详情请看我的回答

长答案:

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

作用域和生命周期经常是相连的;虽然控制在逻辑上位于程序文本的局部范围内的一部分,但已知它是活动的。在查询、lambdas、迭代器块和异步块中使用的局部变量延长了它们的生存期,因此即使控制离开了局部变量的作用域,局部变量仍然存活。本地至少在查询(lambda等)失效之前保持活动状态。

不幸的是,在某些情况下,局部存活的时间可能更长;请看上面的链接问题的例子。有关此问题的详细讨论,请参见这个问题:

c#动作、闭包和垃圾回收

试试下面的

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

您没有指定如果条件不是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样式的版本更有效。