我有一些像这样的代码:
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
此处when Module
始终为true
。为什么?这是出乎意料的行为吗?
答案 0 :(得分:7)
这里Product
是一类,比如说:
Product = Class.new
由于
Product.new.class
#=> Product
您的案例陈述可以简化为
case Product
when Module
'this condition will always be true'
when Product
'i need this to be true, so it never happens'
end
回想一下,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
让我们看看两个逻辑表达式如何评估:
Module === Product #=> true
Product === Product #=> false
注意这是
的语法糖Module.===(Product) #=> true
Product.===(Product) #=> false
检查方法Module#===的文档以查看其工作原理:如果true
是Product
的实例或Module
的实例,则返回Module
}'后代。好吧,是吗?
Product.class #=> Class
Class.ancestors #=> [Class, Module, Object, Kernel, BasicObject]
是的!那怎么样:
Product === Product
Product
是否有方法===
?:
Product.methods.include?(:===)
#=> true
它来自哪里(毕竟我们没有定义它)?我们先来看看:
Product.ancestors
#=> [Product, Object, Kernel, BasicObject]
Object
是否有方法===
?检查文档,我们看到它:Object#===。 1
因此调用了Object#===
。对?我们只是确认一下:
Product.method(:===).owner
#=> Module
糟糕!它来自Module
(既是模块又是类),而不是来自Object
。如上所述,Product
是Class
的实例,Class
是Module
的子类。另请注意,此处===
是Class
(以及Module
) 2 的实例方法:
Class.instance_method(:===).owner
#=> Module
所以Product
陷入了困境。它应该使用Module#===
,它是由Class
继承的实时方法(Module
),还是应该使用Object#===
,它继承自它的超类Object
?答案是优先权与前者相同。
这是Ruby"对象模型"的核心。我不会再这样说了,但我希望我已经为读者提供了一些工具,可以用来弄清楚发生了什么(例如,Object#method和Method#owner。
由于Product === Product
使用Module#===
就像Module == Product
一样,我们通过回答问题确定前者是否返回true
,"是Product
Product
或Product
的后代之一的实例?"。产品没有后代和
Product.class #=> Class,
所以答案是" no",意思是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
然而,除此之外,我经常使用case statement
代替if..elsif..else..end
,因为我觉得它更整洁,更美观:
case
when time == 5pm
feed the dog
when day == Saturday
mow the lawn
...
end
1实际上,此方法可用于所有对象,但通常不可用 调用因为该方法也是为后代定义的。
2为了完全混淆,Class还有一个三等于等级的方法:
Class.method(:===).owner #=> Module
。
答案 1 :(得分:3)
这不是错误。在Ruby中,名为Class
的类是名为Module
的类的子类。因此Class
的每个实例也是Module
。
这里记录了这一点,左栏显示了" Parent":
http://www.ruby-doc.org/core-2.2.0/Class.html
你问这是不好的做法。是。这是一种不好的做法,因为您的case语句中的第一个案例保证会运行,因此这意味着case语句的所有其他情况都是无法运行的无法访问的代码。无法访问的代码是一种不好的做法。最好像这样编写代码:
case something
when Product
# we know that it is a Product
when Customer
# we know that it is a Customer
else
# handle other cases
end
你向我们展示了一些不起作用的代码,然后询问它是否是一种不良的做法,这是非常奇怪的。显然,如果代码在任何情况下根本不起作用,那么代码就是不好的做法!您需要先让代码工作。在它工作之后,你可以尝试考虑代码的不同版本并评估每个代码以查看它是否是"不良实践"或者"良好做法",但这假设那些不同版本实际上有效。
假设你曾经问过如何在Ruby中编写一个方法,将一个数字乘以4。
以下是一些不起作用的代码,所以我们甚至不会讨论是否是不好的做法:
def foo(x)
x * 3
end
以下是一些有效的代码,但它在各种方面都是不好的做法:
def foo(x)
return x + x+ x + x - x + x*1
end
以下是一些有效的代码,并且是很好的做法:
def foo(x)
x * 4
end