了解Ruby中的私有方法

时间:2010-11-27 18:34:51

标签: ruby access-specifier

class Example
 private
 def example_test
  puts 'Hello'
 end
end

e = Example.new
e.example_test

这当然不会起作用,因为我们指定了显式接收器 - 示例(e)的实例,这是针对“私有规则”的。

但我无法理解,为什么人们不能在Ruby中做到这一点:

class Foo
 def public_m
  self.private_m # <=
 end
 private
 def private_m
  puts 'Hello'
 end
end

Foo.new.public_m

public_m方法定义中的当前对象(即self)是Foo的实例。那么为什么不允许呢?要解决此问题,我必须将self.private_m更改为private_m。但为什么这有所不同,self内的public_m Foo实例不是?谁是明字private_m电话的接收者?不是那个self - 实际上你省略了什么,因为Ruby会为你做这件事(会自己调用private_m)?

我希望我没有太多混淆,我仍然对Ruby感到新鲜。


编辑: 谢谢你的所有答案。将它们放在一起我能够(最终)理解显而易见的事情(对于从未见过像Ruby这样的人来说并不那么明显):self本身可以是 显性和隐性接收器,并有所作为。因此,如果要调用私有方法,则有两个规则:self必须是隐式接收器,并且self必须是当前类的实例(在这种情况下为Example - 并且仅发生当self在实例方法定义内部时,在此方法执行期间)。如果我错了,请纠正我。

class Example 

 # self as an explicit receiver (will throw an error)
 def explicit 
  self.some_private_method
 end

 # self as an implicit receiver (will be ok)
 def implicit
  some_private_method
 end

 private

 def some_private_method; end
end

Example.new.implicit

在google trail中可以找到此问题的任何人的消息:这可能会有所帮助 - http://weblog.jamisbuck.org/2007/2/23/method-visibility-in-ruby

7 个答案:

答案 0 :(得分:50)

这是短期和长期的。 Ruby中的私有方法是不能使用显式接收器调用方法,例如some_instance.private_method(值)。因此,即使隐式接收器是self,在您的示例中,您明确使用self,因此无法访问私有方法。

以这种方式思考,您是否希望能够使用已分配给类实例的变量调用私有方法?不。自我是一个变量,所以它必须遵循相同的规则。但是,当您只是在实例中调用该方法时,它会按预期工作,因为您没有明确声明接收器。

Ruby实际上可以使用instance_eval调用私有方法:

class Foo
  private
  def bar(value)
    puts "value = #{value}"
  end
end

f = Foo.new
begin
  f.bar("This won't work")
rescue Exception=>e
  puts "That didn't work: #{e}"
end
f.instance_eval{ bar("But this does") }

希望更清楚一点。

- 编辑 -

我假设你知道这会奏效:

class Foo
 def public_m
  private_m # Removed self.
 end
 private
 def private_m
  puts 'Hello'
 end
end

Foo.new.public_m

答案 1 :(得分:17)

Ruby中private定义是“只能在没有显式接收器的情况下调用”。这就是为什么你只能在没有显式接收器的情况下调用私有方法。没有其他解释。

请注意,规则实际上有一个例外:由于局部变量和方法调用之间的模糊性,以下总是被解析为对局部变量的赋值:

foo = :bar

那么,如果你想调用名为foo=的作家,你会怎么做?好吧,你来添加一个显式接收器,因为没有接收器,Ruby根本就不知道你想要调用方法foo=而不是分配给局部变量{{1} }:

foo

但是如果你想叫一个名为self.foo = :bar 的{​​{1}}作家,你会怎么做?您无法private,因为foo=self.foo =,因此无法使用显式接收器进行调用。好吧,实际上对于这个特定情况(以及本案例单独),你可以实际使用foo=的显式接收者来调用一个private作家。

答案 2 :(得分:13)

这很奇怪,但Ruby的可见性修饰符很多东西都很奇怪。即使self是隐式接收器,实际拼写它也会使它在Ruby运行时的眼睛中显而易见。当它说私有方法无法使用显式接收器调用时,这意味着它,即使self计数。

答案 3 :(得分:3)

IIRC,私有方法只允许 隐式接收器(当然,它总是自己的)。

答案 4 :(得分:1)

对不起我的prevoius回答。我只是不明白你的问题。

我改变了你的代码:

class Foo
 def public_m
  private_m # <=
 end

 def Foo.static_m
   puts "static"
 end

 def self.static2_m
   puts "static 2"
 end

 private 
 def private_m
  puts 'Hello'
 end
end

Foo.new.public_m
Foo.static_m
Foo.static2_m

以下是实例方法的调用:

 def public_m
  private_m # <=
 end

以下是类方法的调用:

 def Foo.static_m
   puts "static"
 end

 def self.static2_m
   puts "static 2"
 end

Foo.static_m
Foo.static2_m

答案 5 :(得分:1)

为User Gates解决方案添加一些增强功能。将私有方法调用到类方法或实例方法几乎是可能的。这是Code Snippets。但不推荐。

类方法

class Example
  def public_m
    Example.new.send(:private_m)
  end

  private
  def private_m
    puts 'Hello'
  end
end

e = Example.new.public_m

实例方法

class Example
  def self.public_m
    Example.new.send(:private_m)
  end

  private
  def private_m
    puts 'Hello'
  end
end

e = Example.public_m

答案 6 :(得分:0)

没有完全回答问题,但您可以通过这种方式调用私有方法

class Example
 private
 def example_test
  puts 'Hello'
 end
end

e = Example.new
e.send(:example_test)