`===`如何工作?

时间:2016-01-09 07:25:17

标签: ruby switch-statement

我看了一下Ruby on Rails的源代码,发现了这样的代码:

case options
when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
  ...
when String
  ...
when :back
  ...
when Proc
  ...
end

其中options可以是StringSymbolProcHash对象。 ===比较仅在一种情况下返回true

'string' === /string/ # => false
:back === :back # => true
(Proc.new {}) === Proc # => nil
Hash.new === Hash # => false

Ruby case如何工作,允许匹配这些不同的情况?

2 个答案:

答案 0 :(得分:4)

您(以及许多初学者)似乎做出的错误假设是===是对称的。实际上并非如此。 x === y的工作方式不同,取决于y,但取决于x

不同的类对===有不同的定义。表达式x === y与:

相同
  • y == x(适用于xStringSymbolHash等的实例。)
  • y =~ x(适用于xRegexp的实例)
  • y.kind_of?(x)(适用于xClass的实例)

此外,您可能会混淆一个类及其实例。 /regexp/ ===Regexp ===不同。 "string" ===String ===等不同。

答案 1 :(得分:3)

可能有助于理解:

"foo" === /foo/

实际上是另一种写作方式:

"foo".===(/foo/)

所以它实际上是对"foo"上的实例方法String#===的方法调用(它是类String的一个实例),并将其作为参数传递给它/foo/。因此,将发生的事情完全由String#===定义。

在您的代码中,实际发生的是:

if /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i === options
  # ...
elsif String === options
  # ...
elsif :back === options
  # ...
elsif Proc === options
  # ...
else
  # ...
end.delete("\0\r\n")

因此,您的case语句实际上是对(按出现顺序)的方法调用:

  1. Regex#===
  2. Module#===
  3. Symbol#===
  4. 然后再次Module#===
  5. 关于你问题中的第二个例子:

    'string' === /string/ # => false
    

    以上是调用String#===,根据文档:

      

    返回str == obj,类似于Object#==。

         

    如果obj不是String的实例但是响应to_str,那么   使用大小写等式Object#===。

    比较两个字符串      

    否则,返回类似于#eql ?,比较长度和内容。

    这就是为什么它不匹配,因为它实际上调用了Object#==

    类似地:

    # this calls `Proc#===`, not `Module#===`
    (Proc.new {}) === Proc # => false
    
    # however, if you want to test for class equality you should do:
    Proc === (Proc.new {}) # => true, calls `Module#===`
    

    同样适用于Hash.new === Hash