obj.nil?与obj == nil

时间:2009-12-29 00:14:21

标签: ruby null comparison

使用obj.nil?obj == nil是否更好?这两者有什么好处?

7 个答案:

答案 0 :(得分:46)

  

使用obj.nil会更好吗?或obj == nil

它完全一样。它具有与外界完全相同的可观察效果(pfff) *

  

以及两者的好处是什么。

如果你喜欢微优化全部,对象会将false返回到.nil?消息,除了对象nil本身,而对象使用{ {1}}消息将进行微小的微观比较  与另一个对象一起确定它是否是同一个对象。

* 查看评论。

答案 1 :(得分:11)

就我个人而言,我更喜欢object.nil?,因为它可能会在较长的行上不那么混乱;但是,如果我在Rails中工作,我通常也会使用object.blank?,因为它还检查变量是否为空。

答案 2 :(得分:5)

除了语法和风格,我想看看"同样的"测试nil的各种方法是。所以,我写了一些基准来看,并在其上投掷各种形式的零测试。

TL; DR - 结果第一

实际结果显示,在所有情况下使用obj作为零检查是最快的。 obj始终比检查obj.nil?快30%或更多。

令人惊讶的是,obj的执行速度是obj == nil的变体的3-4倍,因此似乎会有严重的性能损失。

想要将性能密集型算法加速200%-300%?将所有obj == null支票转换为obj。想要打包代码的性能?尽可能在任何地方使用obj == null。 (开玩笑吧:不要把你的密码包起来!)。

归根结底,请始终使用obj。这符合Ruby Style Guide规则:Don't do explicit non-nil checks unless you're dealing with boolean values.

基准条件

好的,所以这些是结果,那么这个基准如何组合,进行了哪些测试,以及结果的细节是什么?

我提出的零检查是:

  • OBJ
  • obj.nil?
  • !OBJ
  • !! OBJ
  • obj == nil
  • obj!= nil

我选择了各种Ruby类型来测试,以防结果根据类型而改变。这些类型是Fixnum,Float,FalseClass,TrueClass,String和Regex

我在每种类型上使用了这些nil检查条件,以查看它们之间是否存在性能差异。对于每种类型,我测试了nil对象和非nil值对象(例如1_000_000100_000.0falsetrue"string"和{{ 1}})看看在一个nil的对象上检查nil与在一个不是nil的对象上检查nil是否有区别。

基准

完成所有这些后,这里是基准代码:

/\w/

结果

结果很有意思,因为Fixnum,Float和String类型的性能几乎相同,Regex差不多,而FalseClass和TrueClass的执行速度要快得多。在MRI版本1.9.3,2.0.0,2.1.5和2.2.5上进行了测试,各版本的比较结果非常相似。 MRI 2.2.5版本的结果显示在此处(可在the gist中找到:

require 'benchmark'

nil_obj = nil
N = 10_000_000

puts RUBY_DESCRIPTION

[1_000_000, 100_000.0, false, true, "string", /\w/].each do |obj|
  title = "#{obj} (#{obj.class.name})"
  puts "============================================================"
  puts "Running tests for obj = #{title}"

  Benchmark.bm(15, title) do |x|
    implicit_obj_report   = x.report("obj:")            { N.times { obj            } }
    implicit_nil_report   = x.report("nil_obj:")        { N.times { nil_obj        } }
    explicit_obj_report   = x.report("obj.nil?:")       { N.times { obj.nil?       } }
    explicit_nil_report   = x.report("nil_obj.nil?:")   { N.times { nil_obj.nil?   } }
    not_obj_report        = x.report("!obj:")           { N.times { !obj           } }
    not_nil_report        = x.report("!nil_obj:")       { N.times { !nil_obj       } }
    not_not_obj_report    = x.report("!!obj:")          { N.times { !!obj          } }
    not_not_nil_report    = x.report("!!nil_obj:")      { N.times { !!nil_obj      } }
    equals_obj_report     = x.report("obj == nil:")     { N.times { obj == nil     } }
    equals_nil_report     = x.report("nil_obj == nil:") { N.times { nil_obj == nil } }
    not_equals_obj_report = x.report("obj != nil:")     { N.times { obj != nil     } }
    not_equals_nil_report = x.report("nil_obj != nil:") { N.times { nil_obj != nil } }
  end
end

答案 3 :(得分:4)

在许多情况下,都不是,只是测试布尔值真值

尽管这两个操作非常不同,但我非常确定它们总能产生相同的结果,至少在某个人的某个边缘决定覆盖Object的#nil?方法之前。 (一个调用继承自Object的#nil?方法或在NilClass中重写,一个与nil单例进行比较。)

我建议,如果有疑问,你实际上是第三种方式,只是测试一个表达式的真值。

所以,if x而不是if x == nilif x.nil?,以便在表达式值为 false 时进行此测试DTRT。以这种方式工作也可能有助于避免诱使某人将FalseClass#nil?定义为 true

答案 4 :(得分:3)

您可以在nil?上使用Symbol#to_proc,而在x == nil上则不实用。

arr = [1, 2, 3]
arr.any?(&:nil?) # Can be done
arr.any?{|x| x == nil} # More verbose, and I suspect is slower on Ruby 1.9.2
! arr.all? # Check if any values are nil or false

答案 5 :(得分:0)

当你能做到时,我发现自己根本没有使用.nil?

unless obj
  // do work
end

使用.nil?实际上速度较慢,但​​并不明显。 .nil?只是一种检查该对象是否等于nil的方法,除了视觉吸引力和所需的性能很少之外没有区别。

答案 6 :(得分:-1)

Some可能会建议使用.nil?比简单比较要慢,这在你想到它时才有意义。

但如果你不关注规模和速度,那么.nil?可能更具可读性。