从Ruby中的实例方法调用受保护的类方法

我一直有这个烦人的反复出现的主题; 我只想说,我有一个定义实例方法和受保护类方法的类。 实例方法必须调用类方法。 为了做到这一点,我必须打破可见性规则并使用危险的“发送”function。 像这样的东西:

class Bang def instance_bang self.class.send(:class_band) end protected def self.class_bang puts "bang" end end 

我发现这很糟糕,因为类方法应该在类范围内使用,因此应该在其中保持可见和可调用,对吧? 是否有另一种方法在实例方法中使用类方法需要依赖“发送”function,因此不会破坏可见性?

更新:

根据Sergio Tulentsev的回复(thx for correction),我将用一个代码片段来更新我的担忧,该代码片段总结了我对方法可见性的关注,同时仍然在已经定义的范围内。

 class Bang def instance_bang private_bang = 1 self.private_bang(private_bang) end private def private_bang(p) puts "bang" p end end 

调用Bang.new.instance_bang会引发exception,除非你在private_bang调用上使用send(这次我检查了:))。

编辑:回答更新的问题

禁止使用显式接收器调用私有方法。 您必须使用隐式接收器( private_bang ,不使用self )或使用send 。 有关更多信息,请参阅我的另一个答案 。

顺便说一下,最初的问题是从实例方法调用类实例方法。 您的澄清不包括此。 但是,如果仍然如此,则必须使用self.class.send或将该方法设为公共(以便您可以使用显式接收器)。

让我们考虑一个私有类方法(因为受保护的类方法没有意义 )。

我们知道实例可以自己调用私有方法,只要它不使用显式接收器( self.call_something_private )。 看来你也期望一个实例可以在它自己的类上调用私有类方法,但事实并非如此。

让我们看看如何在不使用send情况下执行此操作。

privateprotected宏仅影响当前作用域的实例方法,而不影响类方法。 以下是重写原始代码的三种方法:

 class Bang def instance_bang self.class.class_bang end # declare method visibility after def self.class_bang puts "bang" end private_class_method :class_bang # inline private_class_method def self.class_bang puts "bang" end # class scope class << self # the private macro works here because we're inside the class scope private def class_bang puts "bang" end end end 

所以现在我们想在类上公开一个接口来调用class_bang ,但是只有当它被Bang实例调用时才会这样。

 class Bang def instance_bang self.class.invoke_class_bang(self) end class << self private def class_bang puts "bang" end public # we ask the receiver to pass itself as an argument ... def invoke_class_bang(receiver) # ... so that we can check whether it's class_bang if receiver.is_a?(Bang) end end end 

这不是一个非常漂亮的解决方案。 这是一个狡猾的方式:

 class Bang def initialize def self.instance_bang() self.class.method(:class_bang).call end end class << self private def class_bang puts "bang" end end end 

“类方法应该在类范围内使用,因此应该在其中保持可见和可调用,对吧?” 是的,这是正确的,这就是Ruby展示的行为。 (作为澄清的一点,实例范围不是“在”类范围内。它们是适当的,是分开的。)

send解决方案是子类或重新打开类以添加公共类方法来访问受保护的类方法。