可以将此语句转换为1 lambda表达式吗?

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

最佳答案

这种查询要非常小心。 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的当前值(如果存在)匹配,无论在构建查询时是否存在一个。

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


  因此,可以说我也返回了此查询,该查询允许在方法之外对查询进行评估。由于过滤器超出范围,是否会导致崩溃?


简短答案:

没有。

更长的答案:

这是这个问题的重复:

Action/Lambda Expression Memory Management Question

有关详细信息,请参见我的答案。

长答案:

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

范围和生存期通常是联系在一起的;虽然控制逻辑上是程序文本的一部分,而局部区域在此范围内,但已知该控件仍处于活动状态。查询,lambda,迭代器块和异步块中使用的局部变量的生存期得到延长,因此,即使控制离开了局部变量的范围,局部变量仍然有效。本地至少在查询,lambda等死亡之前一直存在。

不幸的是,在某些情况下,当地居民的生存时间可能更长。有关示例,请参见上面的链接问题。有关此问题的扩展讨论,请参见以下问题:

C# Action, Closure, and Garbage Collection

09-10 03:54
查看更多