当对象为null VS Linq to SQL时,Linq to对象

我有这个Linq来对象查询:

var result = Users.Where(u => u.Address.Country.Code == 12) 

如果Address或Country为null,我会收到exception。
为什么这个查询不检查地址是否为空并且只是在那之后? 这样我就不需要写这个可怕的查询了:

 var result = Users.Where(u => u.Address != null && u.Address.Country != null && u.Address.Country.Code == 12) 

在Linq to SQL中,第一个查询将完成工作(当然是出于其他原因)。

是一种避免linq中的“空检查”对象的方法吗?

不幸的是,在C#(以及许多其他编程语言)中,“null”被不一致地处理。 可空算术被解除 。 也就是说,如果你对可空整数进行算术运算,并且没有操作数为空,那么你得到正常的答案,但是如果它们中的任何一个为空,你就得到null。 但是“成员访问”操作员没有解除; 如果你给成员访问“。”一个空操作数。 运算符,它抛出exception而不是返回空值。

如果我们从头开始设计一个类型系统,我们可以说所有类型都是可空的,并且任何包含空操作数的表达式都会产生一个null结果,而不管操作数的类型如何。 因此,使用null接收器或null参数调用方法将产生null结果。 这个系统很有意义,但显然我们现在实施它已经太迟了; 已经编写了数百万行代码来预期当前的行为。

我们考虑过添加一个“提升”的成员访问操作符,可能是标记的.? 。 那么你可以说where user.?Address.?Country.?Code == 12 ,这将产生一个可空的int,然后可以将其作为可空的int通常比较为12。 然而,这一点从来没有超过“是的,在未来的版本中可能很好”的设计过程阶段,所以我不希望它很快。


更新:上面提到的“Elvis”运算符是在C#6.0中实现的。

不,它是一个空引用exception,就像访问var x = u.Address.Country.Code; 将是一个NullReferenceException

您必须始终确保在LINQ to对象中取消引用的内容不为null ,就像使用任何其他代码语句一样。

您可以使用您拥有的&&逻辑来执行此操作,也可以链接Where子句(尽管这将包含更多迭代器并且可能执行速度更慢):

 var result = Users.Where(u => u.Address != null) .Where(u.Address.Country != null) .Where(u.Address.Country.Code == 12); 

注意 :下面的Maybe()方法仅作为“你也可以”方法提供,我不是说它的好坏,只是展示一些人所做的事情。 如果你不喜欢Maybe()请不要低价投票Maybe()我只是重复我见过的各种解决方案……

我已经看到一些编写的Maybe()扩展方法可以让你做你想做的事情。 有些人喜欢/不喜欢这些,因为它们是在null引用上运行的扩展方法。 我不是说那是好还是坏,只是有些人觉得违反了良好的OO行为。

例如,您可以创建一个扩展方法,如:

 public static class ObjectExtensions { // returns default if LHS is null public static TResult Maybe(this TInput value, Func evaluator) where TInput : class { return (value != null) ? evaluator(value) : default(TResult); } // returns specified value if LHS is null public static TResult Maybe(this TInput value, Func evaluator, TResult failureValue) where TInput : class { return (value != null) ? evaluator(value) : failureValue; } } 

然后做:

 var result = Users.Where(u => u.Maybe(x => x.Address) .Maybe(x => x.Country) .Maybe(x => x.Code) == 12); 

从本质上讲,这只是将链中的null级联(或者在非引用类型的情况下为默认值)。

更新

如果您想提供非默认的失败值(如果任何部分为空,则代码为-1),您只需将新的失败值传递给Maybe():

 // if you wanted to compare to zero, for example, but didn't want null // to translate to zero, change the default in the final maybe to -1 var result = Users.Where(u => u.Maybe(x => x.Address) .Maybe(x => x.Country) .Maybe(x => x.Code, -1) == 0); 

就像我说的,这只是众多解决方案中的一种。 有些人不喜欢能够从null引用类型调用扩展方法,但是有些人倾向于使用它来解决这些null级联问题。

但是,目前C#中没有内置的null-de-reference运算符,所以要么像以前一样使用条件空值检查,要么将Where()语句链接起来,以便它们过滤掉null或者构建让你像上面的Maybe()方法一样级联null