我希望修补Set#add
接受多个参数,方法与Array#push
相同。以下内容:
require 'set'
class Set
def add(*args)
args.each { |arg| super arg }
self
end
end
s = Set.new
s.add 1,2
不起作用,它引起了:
'阻止添加':超级:没有超类方法`add' #(NoMethodError)
我不知道为什么会出现这种情况,因为
我在Set#add
来源中看到它有一个标准定义。它是一个如此简单的定义,我可以轻松使用super
:
require 'set'
class Set
def add(*args)
args.each { |arg| @hash[arg] = true }
self
end
end
s = Set.new
s.add 1,2
为什么拨打super
无效?
答案 0 :(得分:5)
为什么拨打
super
无效?
这是因为Set#add
在Set
本身上定义,而不在其任何超类上定义。并且你要覆盖这个定义。
答案 1 :(得分:3)
如果您覆盖某个方法,那么您需要更改其定义。致电super
将无效;原始方法 not 仍然存在作为转发消息调用的东西。
有几种方法可以解决这个问题。 "经典"方法是根本不重写方法,而是定义自己的Set
的子类:
require 'set'
class MySet < Set
def add(*args)
args.each { |arg| super arg }
self
end
end
s = MySet.new
s.add 1,2
#=> #<MySet: {1, 2}>
...但这可能并不理想,因为您需要在整个代码库中引用MySet
(或任何您称之为的内容),而不是Set
。
如果您想直接修改Set
的行为,那么&#34;老派&#34; ruby方式(不再推荐,如ruby v2.0+
)是将原始方法alias
改为另一个名称,然后创建自己的版本并调用原始方法: / p>
require 'set'
class Set
alias_method :original_add, :add
def add(*args)
args.each { |arg| original_add(arg) }
self
end
end
s = Set.new
s.add 1,2
#=> #<Set: {1, 2}>
Rails还用于提供帮助方法:alias_method_chain
,这使得执行这种常见的解决方法变得更容易。
但很明显,这不是一个很好的解决方案,因为你最终会用original_<foo>
或<foo>_with(out)_<feature>
等奇怪的方法来污染课程。值得庆幸的是,Ruby 2.0添加了一种新方法:Module#prepend
,它为问题提供了更清晰的解决方案:
require 'set'
module AddWithMultipleArgs
def add(*args)
args.each { |arg| super arg }
self
end
end
class Set
prepend AddWithMultipleArgs
end
# Or, you can just call:
# Set.prepend(AddWithMultipleArgs)
s = Set.new
s.add 1,2
#=> #<Set: {1, 2}>
这可以通过在课程中注入新方法 原始版本之前的方法来实现。祖先链:
Set.ancestors
#=> [AddWithMultipleArgs, Set, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
由于您从未修改过方法的原始版本,因此在Set
类本身上,调用super
将按预期工作。