可以将此语句转换为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