这种使用案例陈述的做法是不好的吗?
Is this use of case statement a bad practice?
我有一些这样的代码:
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
。为什么?这是意外行为吗?
这不是错误。在Ruby中,名字为Class
的class是名字为Module
的class的子class。因此 Class
的每个实例也是 Module
.
这在此处记录,在左栏中显示 "Parent":
http://www.ruby-doc.org/core-2.2.0/Class.html
你问这是否是一种不好的做法。是的。这是一个不好的做法,因为你的 case 语句中的第一个 case 保证 运行,所以这意味着 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
切线评论
您向我们展示了一些不起作用的代码,然后询问它是否是 "bad practice",这很奇怪。显然,如果代码在任何情况下都不起作用,那么它就是一种糟糕的做法!你需要先让你的代码工作。在它工作之后,您可以尝试考虑不同版本的代码并评估每个版本以查看它是 "bad practice" 还是 "good practice",但前提是这些不同版本确实有效。
假设您问的是如何在 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
啊,这么简单的问题。但是是吗?
这里Product
是一些class,说:
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
语句使用方法 ===
来确定哪个 object 到 return,这意味着您的 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#=== 的文档以了解其工作原理:它 returns 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
(既是模块又是 class),而不是来自 Object
。正如我们在上面看到的,Product
是 Class
的实例,而 Class
是 Module
的子 class。另请注意,此处 ===
是 Class
(和 Module
)2:
的实例方法
Class.instance_method(:===).owner
#=> Module
于是Product
进退两难。它应该使用 Module#===
,一个由 parent (Class
) 提供的实例方法,它继承自 Module
,还是应该使用 Object#===
,即它继承自它的superclass, Object
?答案是前者优先
这是 Ruby 的 "object model" 的核心。我不会再多说了,但我希望我已经为读者提供了一些工具,他们可以使用这些工具来弄清楚发生了什么(例如,Object#method and Method#owner.
由于 Product === Product
与 Module == Product
一样使用 Module#===
,我们通过回答问题来确定前者 return 是否为 true
," 是 Product
Product
或 Product
的后代之一的实例?”。产品没有后代并且
Product.class #=> Class,
所以答案是"no",意思是Product === Product
returns false
.
编辑: 我发现我忘了实际回答标题中提出的问题。我想这需要一个意见(SO no-no),但我认为 case
陈述是自切片面包以来最伟大的事情。当需要使用 ===
或 ==
将各种值与参考值(例如,变量的内容或通过方法 return 编辑的值)进行比较时,它们特别有用。例如(参见 Fixnum#===, where ===
is equivalent to ==
--note the typo in the docs, Regexp#=== and 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 其实这个方法是所有object都可以用的,只是不是一般
被调用是因为该方法也是为后代定义的。
2 为了彻底混淆,Class 也有一个 triple-equals class 方法:
Class.method(:===).owner #=> Module
.
我有一些这样的代码:
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
。为什么?这是意外行为吗?
这不是错误。在Ruby中,名字为Class
的class是名字为Module
的class的子class。因此 Class
的每个实例也是 Module
.
这在此处记录,在左栏中显示 "Parent":
http://www.ruby-doc.org/core-2.2.0/Class.html
你问这是否是一种不好的做法。是的。这是一个不好的做法,因为你的 case 语句中的第一个 case 保证 运行,所以这意味着 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
切线评论
您向我们展示了一些不起作用的代码,然后询问它是否是 "bad practice",这很奇怪。显然,如果代码在任何情况下都不起作用,那么它就是一种糟糕的做法!你需要先让你的代码工作。在它工作之后,您可以尝试考虑不同版本的代码并评估每个版本以查看它是 "bad practice" 还是 "good practice",但前提是这些不同版本确实有效。
假设您问的是如何在 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
啊,这么简单的问题。但是是吗?
这里Product
是一些class,说:
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
语句使用方法 ===
来确定哪个 object 到 return,这意味着您的 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#=== 的文档以了解其工作原理:它 returns 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
(既是模块又是 class),而不是来自 Object
。正如我们在上面看到的,Product
是 Class
的实例,而 Class
是 Module
的子 class。另请注意,此处 ===
是 Class
(和 Module
)2:
Class.instance_method(:===).owner
#=> Module
于是Product
进退两难。它应该使用 Module#===
,一个由 parent (Class
) 提供的实例方法,它继承自 Module
,还是应该使用 Object#===
,即它继承自它的superclass, Object
?答案是前者优先
这是 Ruby 的 "object model" 的核心。我不会再多说了,但我希望我已经为读者提供了一些工具,他们可以使用这些工具来弄清楚发生了什么(例如,Object#method and Method#owner.
由于 Product === Product
与 Module == Product
一样使用 Module#===
,我们通过回答问题来确定前者 return 是否为 true
," 是 Product
Product
或 Product
的后代之一的实例?”。产品没有后代并且
Product.class #=> Class,
所以答案是"no",意思是Product === Product
returns false
.
编辑: 我发现我忘了实际回答标题中提出的问题。我想这需要一个意见(SO no-no),但我认为 case
陈述是自切片面包以来最伟大的事情。当需要使用 ===
或 ==
将各种值与参考值(例如,变量的内容或通过方法 return 编辑的值)进行比较时,它们特别有用。例如(参见 Fixnum#===, where ===
is equivalent to ==
--note the typo in the docs, Regexp#=== and 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 其实这个方法是所有object都可以用的,只是不是一般 被调用是因为该方法也是为后代定义的。
2 为了彻底混淆,Class 也有一个 triple-equals class 方法:
Class.method(:===).owner #=> Module
.