为什么我不能在lambda表达式中使用空值传播运算符?

118

我经常在我的代码中使用空值传播运算符,因为它让我的代码更易读,特别是在长查询中,我不必对每个使用的类进行 null 检查。

以下代码会抛出编译错误,因为我们无法在 Lambda 中使用空值传播运算符。

var cnt = humans.AsQueryable().Count(a => a.House?[0].Price == 5000);

错误:

Error CS8072 表达式树的 lambda 表达式不允许包含空传播运算符。

如果真的无法做其他处理,C# 可以将上述代码轻松地转换为以下代码!

var cnt = humans.AsQueryable().Count(a => a.House != null && a.House[0].Price == 5000);

我很好奇为什么C#什么都没做,只是简单地抛出了一个编译器错误?


7
Foo?.Bar并不等同于Foo != null ? Foo.Bar : null,因为使用空值传播运算符时Foo只被评估一次,而使用条件运算符时Foo被评估了两次,因此直接翻译可能在某些情况下不正确。 - Lucas Trzesniewski
5
请注意,如果是 EF 的代码,有可能你并不需要使用空值传播运算符,因为当查询转换为 SQL 调用时,SQL 不会抛出 null 值 :-) - xanatos
请注意:最好写成 var q = from c in Categories join p in Products on c equals p.Category into ps from p in ps.DefaultIfEmpty() select new { Category = c, ProductName = (p?.ProductName)??"(No products)"}; 而不是写成 ProductName = (p == null) ? "(No products)" : p.ProductName,因为 EF 目前不支持 ?. 运算符。 - Matt
1个回答

88

这很复杂,因为表达式树lambda(不像委托lambda)是由已存在的LINQ提供程序解释的,这些提供程序尚不支持空传播。

转换为条件表达式并不总是准确的,因为有多个评估,而使用?.只有一个评估,例如:

customer.Where(a => c.Increment()?.Name) // Written by the user 
customer.Where(a => c.Increment() == null ? null : c.Increment().Name) // Incorrectly interpreted by an old LINQ provider

你可以在CodePlex上相关的讨论中深入了解,其中提供了3种解决方案:NullPropagationExpressionConditionalExpression和混合方案。

36
如果某些查询提供程序无法支持这一点,我肯定不会感到惊讶,但这并不是不支持C#语言的理由。 - Servy
24
某些查询提供商目前不支持它并不是禁止所有查询提供商永远无法使用它的理由。 - Servy
12
显然,任何查询提供程序都不会花时间支持处理这样的请求,直到该提供程序的用户能够实际创建表示它的表达式树。为了支持这一点,需要的第一件事是让 lambda 能够表示它。在那之后,查询提供程序可以开始支持它,视情况而定。此外,有很多提供程序在做各种不同的事情。并不像 EF 是世界上唯一的查询提供程序。 - Servy
14
“Expression”的整个意图是能够将所有C#表达式在代码中语义化地表示出来。 它的设计不仅仅是语言的一小部分。 - Servy
9
似乎这个问题三年后仍未得到解决 - 微软现在难道还没有找到时间吗?他们似乎有一个坏习惯,就是把时间和资源作为借口,只半实现 C# 的新功能。 - NetMage
显示剩余24条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接