Ruby类类型和case语句

171

什么是两者之间的区别?

case item.class
when MyClass
  # do something here
when Array
  # do something different here
when String
  # do a third thing
end

而且
case item.class
when MyClass.class
  # do something here
when Array.class
  # do something different here
when String.class
  # do a third thing
end

由于某些原因,第一个有时有效而第二个无效,而其他时候,第二个有效而第一个无效。为什么?哪种方法是“正确”的?


2
String是一个类。一个类的类是Class。 - Volte
1
请注意,MyClass === obj 使用 Module#=== 方法来检查 obj 是否是 MyClass 的实例。 - builder-7000
对我来说,这些答案都没有意义。根据我的实验,使用类对象在when/case结构中实际上是与值的类进行比较,而不是与值本身进行比较。 - undefined
5个回答

282

1
谢谢!很抱歉重复提问,但是我进行了几次搜索都没有找到之前的问题。现在看来,使用 case 语句时 === 的问题相当普遍。这应该在教程中更经常地指出(但我敢打赌许多教程作者也不知道这一点)。 - Daisy Sophia Hollman
7
使用ActiveRecord时,有一个要注意的地方尚未提到。在类比较中使用ActiveRecord === 方法时,会使用 .is_a? 方法,这意味着类的子类将在case语句中被视为true。说明:此段文字讨论了在使用Rails框架中的ActiveRecord时需要注意的问题。在类比较中使用ActiveRecord的===方法时,会与.is_a?方法相关联,因此该类的所有子类也会被视为true。 - Jeremy Baker
是的,这在ActiveModel中也不起作用,这是一个相当大的陷阱! - Hackeron

78

是的,Nakilon是正确的,您必须了解在when子句中给定的对象上三等 === 运算符的工作方式。 在Ruby中

case item
when MyClass
...
when Array
...
when String
...

真的很

if MyClass === item
...
elsif Array === item
...
elsif String === item
...

理解 case 语句调用三等号方法(例如 MyClass.===(item)),并且该方法可以被定义为任何您想要的操作,然后您可以使用 case 语句进行精确匹配。


1
如果我有 arr = [],那么我注意到 if Array === arr 将会被评估为真,但是 if arr === Array 将会被评估为假。能否有人帮忙解释一下? - DDD
6
"==="只是一种方法,可以由类的设计者定义为执行任何操作。请注意,“a === b”实际上意味着“a.=== b”,因此如果你交换了a和b的位置,可能会得到不同的结果。没有保证"==="是可交换的。事实上,Array === Array是false,但Object === Object是true,因此Array正在重新定义"==="的语义。 - Fred

25

您可以使用:

case item.class.to_s
    when 'MyClass'

...当以下转折不可能时:

case item
    when MyClass

这是因为case使用===,而 === 运算符所描述的关系不是交换的。例如,5是一个整数,但整数5吗?这就是你应该考虑case/when的方式。


2
“5是一个整数,但整数是5吗?”这是一个非常人性化的解释,谢谢。 - Matt

7
在Ruby中,一个类名是一个常量,它指向一个描述特定类的类型为“Class”的对象。这意味着在Ruby中说“MyClass”等同于在Java中说“MyClass.class”。
“obj.class”是类型为“Class”的一个对象,用来描述“obj”的类。如果“obj.class”是“MyClass”,那么“obj”就是使用“MyClass.new”创建的(粗略地说)。“MyClass”是一个类型为“Class”的对象,它描述了使用“MyClass.new”创建的任何对象。
“MyClass.class”是“MyClass”对象的类(它是描述使用“MyClass.new”创建的任何对象类型为“Class”的对象的类)。换句话说,“MyClass.class == Class”。

啊!现在我对Ruby元编程了解得更多,这就有意义了。 - Narfanator

1

这取决于您的item变量的性质。如果它是一个对象实例,例如:

t = 5

那么

t.class == Fixnum

但如果它本身是一个类,例如

t = Array

那么它将是一个Class对象,因此

t.class == Class

编辑: 请参考Nakilon提供的如何在“case when”中捕获Errno::ECONNRESET类?,因为我的答案可能是错误的。


1
在 Ruby 中,一切都是"一个对象的实例"。 - Eric Duminil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接