对Linq查询惯用的空值检查?

Sim*_*ine 7 c# sql linq linq-to-sql

使用Linq-to-SQL,我想知道哪个更适合以下三个,

  • foos.Where(foo => foo.Bar.HasValue && foo.Bar.Value < 42)
  • foos.Where(foo => foo.Bar.Value < 42)
  • foos.Where(foo => foo.Bar < 42)

第一个选项生成一个额外的Bar IS NOT NULL谓词,可能在大多数DBMS中被优化掉了.如果一个查询对象而不是数据库,则null检查将是必需的,但由于可以创建IQueriable<Foo>可能在对象上失败而在数据库上失败的泛型查询,因此第一个选项始终有效,尽管Linq和SQL代码都是比第二个选项长一点.Michael Liu提供的第三个选项似乎是两个世界中最好的选择,但是在foo.Bar类型为bool的情况下不会起作用吗?:( foos.Where(foo => foo.Bar)导致类型错误,因为此处未进行隐式转换).

是否应该努力编写通用查询,如果在最初设计的上下文之外使用它们将不会失败?

Mic*_*Liu 2

第三种选择是使用“提升”<运算符,如果任一操作数为空,则返回 false。这使您可以省略对 null 的显式检查,并且适用于所有 LINQ 提供程序:

foos.Where(foo => foo.Bar < 42)
Run Code Online (Sandbox Code Playgroud)

如果 的编译时类型为foosIEnumerable<Foo>foo.Bar < 42相当于

foo.Bar.GetValueOrDefault() < 42 && foo.Bar.HasValue
Run Code Online (Sandbox Code Playgroud)

只不过foo.Bar只访问一次。(我通过在 Reflector 中反编译程序得到了这一点。有趣的是,编译器选择在检查 null之前执行比较,但作为微优化,这是有意义的。)

如果 的编译时类型为foosIQueryable<Foo>foo.Bar < 42相当于

foo.Bar < (int?)42
Run Code Online (Sandbox Code Playgroud)

是否以及如何检查空值取决于 LINQ 提供程序。


更新:如果 的类型foo.Barbool?并且您只想包含为foo.Bartrue 的记录(即既不是 false 也不是 null),那么您可以编写

foos.Where(foo => foo.Bar == true)
Run Code Online (Sandbox Code Playgroud)

或者

foos.Where(foo => foo.Bar.GetValueOrDefault())
Run Code Online (Sandbox Code Playgroud)

  • @Aducci 这是一个 lambda,lambda 是 C# 中极少数与上下文相关的表达式之一(也就是说,该表达式的编译方式及其含义取决于该表达式的使用方式)。如果“foos”是“IEnumerable&lt;T&gt;”,则“Enumerable.Where”将期望“Func&lt;T, bool&gt;”作为第一个参数,这意味着 lambda 将被编译为该委托类型。如果“foos”是“IQueryable&lt;T&gt;”,它将调用“Queryable.Where”并期望“Expression&lt;Func&lt;T, bool&gt;&gt;”,因此 lambda 将被编译为封装该委托类型的表达式。 (2认同)