我有这个Linq对象查询:
var result = Users.Where(u => u.Address.Country.Code == 12)
如果地址或国家/地区为空,我会收到例外情况 为什么这个查询不会检查地址是否为空并且只是在该地址之后? 这样我就不需要写这个可怕的查询了:
var result = Users.Where(u => u.Address != null &&
u.Address.Country != null &&
u.Address.Country.Code == 12)
在Linq to SQL中,第一个查询将完成工作(当然是出于其他原因)。
是否可以避免linq中的“空检查”对象?
答案 0 :(得分:6)
不幸的是,在C#(以及许多其他编程语言)中,“null”处理不一致。可空算术解除。也就是说,如果你对可空整数进行算术运算,并且没有操作数为空,那么你得到正常的答案,但是如果它们中的任何一个为空,你就得到null。但“成员访问”操作符不解除;如果你给成员访问“。”一个空操作数。运算符,它抛出异常而不是返回空值。
如果我们从头开始设计一个类型系统,我们可能会说所有类型都是可空的,并且包含空操作数的任何表达式都会产生null结果,而不管操作数的类型。因此,使用null接收器或null参数调用方法将产生null结果。这个系统很有意义,但显然我们现在实施它已经太晚了;已经编写了数百万行代码来预期当前的行为。
我们考虑过添加一个“提升”的成员访问运算符,可能标注为.?
。所以你可以说where user.?Address.?Country.?Code == 12
然后会生成一个可以为空的int,然后可以将其作为可空的int通常比较为12。然而,这一点从来没有超过“是的,在未来的版本中可能很好”的设计过程阶段,所以我不会很快就会想到它。
更新:上面提到的“Elvis”运算符是在C#6.0中实现的。
答案 1 :(得分:5)
不,它是一个空引用异常,就像访问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()
扩展方法,可以让你做你想做的事情。有些人喜欢/不喜欢这些因为它们是在null
引用上运行的扩展方法。我不是说那是好还是坏,只是有些人觉得违反了类似OO的行为。
例如,您可以创建一个扩展方法,如:
public static class ObjectExtensions
{
// returns default if LHS is null
public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator)
where TInput : class
{
return (value != null) ? evaluator(value) : default(TResult);
}
// returns specified value if LHS is null
public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> 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
,或构建一些内容,让您像上面的null
方法一样级联Maybe()
。