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

时间:2015-01-30 23:54:08

标签: 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

此处when Module始终为true。为什么?这是出乎意料的行为吗?

2 个答案:

答案 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#===的文档以查看其工作原理:如果trueProduct的实例或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。如上所述,ProductClass的实例,ClassModule的子类。另请注意,此处===Class(以及Module 2 的实例方法:

Class.instance_method(:===).owner
  #=> Module

所以Product陷入了困境。它应该使用Module#===,它是由Class继承的实时方法(Module),还是应该使用Object#===,它继承自它的超类Object?答案是优先权与前者相同。

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

由于Product === Product使用Module#===就像Module == Product一样,我们通过回答问题确定前者是否返回true,"是Product ProductProduct的后代之一的实例?"。产品没有后代和

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
相关问题