这种案例陈述的使用是不好的做法吗?

okl*_*liv 2 ruby ruby-on-rails-4.1

我有一些像这样的代码:

case Product.new.class # ActiveRecord instance class => Product
when Module
  'this condition will always be true'
when Product
  'i need this to be true, but first condition is always true, so it never happens'
end
Run Code Online (Sandbox Code Playgroud)

when Module总是这里true.为什么?这是出乎意料的行为?

Car*_*and 7

啊,这样一个看似简单的问题.但是吗?

Product是一些课,说:

Product = Class.new
Run Code Online (Sandbox Code Playgroud)

以来

Product.new.class
  #=> Product
Run Code Online (Sandbox Code Playgroud)

您的案例陈述可以简化为

case Product
when Module
  'this condition will always be true'
when Product
  'i need this to be true, so it never happens'
end
Run Code Online (Sandbox Code Playgroud)

回想一下,该case语句使用该方法===来确定要返回的对象,这意味着您的case语句等效于

if Module === Product
  'this condition will always be true'
elsif Product === Product
  'i need this to be true, so it never happens'
end
Run Code Online (Sandbox Code Playgroud)

让我们看看两个逻辑表达式如何评估:

Module  === Product  #=> true 
Product === Product  #=> false 
Run Code Online (Sandbox Code Playgroud)

请注意,这是语法糖

Module.===(Product)  #=> true
Product.===(Product) #=> false
Run Code Online (Sandbox Code Playgroud)

检查方法Module#===的文档以查看它是如何工作的:它返回trueif Product的一个ModuleModule后代的实例.好吧,是吗?

Product.class   #=> Class 
Class.ancestors #=> [Class, Module, Object, Kernel, BasicObject] 
Run Code Online (Sandbox Code Playgroud)

它是!那么怎么样:

Product === Product
Run Code Online (Sandbox Code Playgroud)

是否Product有一种方法===

Product.methods.include?(:===)
  #=> true
Run Code Online (Sandbox Code Playgroud)

它来自哪里(毕竟我们没有定义它)?我们先来看看:

Product.ancestors
  #=> [Product, Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)

是否Object有一种方法===?检查文档,我们看到它: Object#===.1

所以Object#===被调用.对?让我们确认一下:

Product.method(:===).owner
  #=> Module
Run Code Online (Sandbox Code Playgroud)

哎呦!它来自Module(既是模块又是类),而不是来自Object.正如我们在上面看到的,Product是和的一个实例,Class并且Class是它的子类Module.另请注意,这===Class(和Module)2的实例方法:

Class.instance_method(:===).owner
  #=> Module
Run Code Online (Sandbox Code Playgroud)

Product陷入困境也是如此.它是否应该使用Module#===,由它的parent(Class)提供的实例方法,从它继承的超级类继承它Module,或者它应该Object#===继承它,Object?答案是优先权与前者相同.

这是Ruby的"对象模型"的核心.我不再赘述,但我希望我已经为读者提供了一些可以用来弄清楚发生了什么的工具(例如,Object#方法Method#owner.

既然Product === Product使用Module#===同样Module == Product如此,我们true通过回答问题来确定前者是否返回,"是Product一个ProductProduct后代之一的实例?".产品没有后代和

Product.class #=> Class,
Run Code Online (Sandbox Code Playgroud)

所以答案是"不",意味着Product === Product回报false.

编辑:我看到我忘了实际回答标题中提出的问题.这需要一个我认为的意见(不可否认),但我认为case陈述是自切片面包以来最伟大的事情.它们特别有用,当一个人需要不同的值与参考值(例如,一个可变的或由方法返回的值的内容)使用=====.例如(参见Fixnum#===,其中===相当于 - ==记录文档中的拼写错误,Regexp#===Range#===):

str =
case x
when 1                   then 'cat'
when 2,3                 then 'dog'
when (5..Float#INFINITY) then 'cow'
else                          'pig'
end

result =
case obj
when String
  ...
when Array
  ...
end

case str
when /\d/
  ...
when /[a-z]/
  ...
end
Run Code Online (Sandbox Code Playgroud)

然而,除此之外,我经常使用一个case statement,if..elsif..else..end因为我觉得它更整洁,更美观:

case
when time == 5pm
  feed the dog
when day == Saturday
  mow the lawn
...
end
Run Code Online (Sandbox Code Playgroud)

1实际上,此方法可用于所有对象,但通常不会调用,因为该方法也是为后代定义的.

2为了彻底混淆,Class还有一个三等于等级的方法: Class.method(:===).owner #=> Module.