Понимание частных методов в Ruby - PullRequest
49 голосов
/ 27 ноября 2010
class Example
 private
 def example_test
  puts 'Hello'
 end
end

e = Example.new
e.example_test

Это, конечно, не будет работать, потому что мы указали явный получатель - экземпляр Example (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 экземпляр Foo внутри public_m? А кто получатель голого private_m звонка? Разве это не self - что на самом деле вы опускаете, потому что Ruby сделает это за вас (вызовет private_m для себя)?

Надеюсь, я не слишком это запутал, я все еще новичок в Ruby.


EDIT: Спасибо за все ответы. Собрав их все вместе, я смог (наконец) ухватиться за очевидное (и не столь очевидное для кого-то, кто никогда не видел таких вещей, как Руби): что 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 Trails: это может быть полезно - http://weblog.jamisbuck.org/2007/2/23/method-visibility-in-ruby

Ответы [ 7 ]

51 голосов
/ 27 ноября 2010

Вот короткая и длинная часть.То, что частное средство в Ruby является методом, нельзя вызывать с явными получателями, например, some_instance.private_method (value).Таким образом, даже если неявный получатель является self, в вашем примере вы явно используете 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
17 голосов
/ 28 ноября 2010

Определение из private в Ruby: «можно вызывать только без явного получателя».И именно поэтому вы можете вызывать только закрытые методы без явного получателя.Других объяснений нет.

Обратите внимание, что на самом деле есть исключение из правила: из-за неоднозначности между локальными переменными и вызовами методов следующее всегда будет разрешено как назначениев локальную переменную:

foo = :bar

Итак, что вы будете делать, если хотите вызвать писателя с именем foo=?Ну, у вас есть для добавления явного получателя, потому что без получателя Ruby просто не узнает, что вы хотите вызвать метод foo= вместо присвоения локальной переменной foo:

self.foo = :bar

Но что вы будете делать, если хотите позвонить писателю private по имени foo=?Вы не можете написать self.foo =, потому что foo= равно private и, следовательно, не могут быть вызваны с явным получателем.Ну, на самом деле для этого особого случая (и только для этого случая ) вы можете фактически используете явный получатель self для вызова private пишущего.

14 голосов
/ 27 ноября 2010

Это странно, но многие вещи в модификаторах видимости Ruby странные. Даже если self является неявным получателем, его фактическое изложение делает его явным в глазах среды выполнения Ruby. Когда он говорит, что частные методы не могут быть вызваны с явным получателем, это означает, что даже self считается.

3 голосов
/ 27 ноября 2010

IIRC, приватные методы позволяют только неявный получатель (который, конечно, всегда сам).

1 голос
/ 09 февраля 2018

Добавление некоторых улучшений в решение User Gates. Вызов частного метода для метода класса или метода экземпляра в значительной степени возможен. Вот фрагменты кода. Но не рекомендуется.

Метод класса

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
1 голос
/ 27 ноября 2010

Извините за мой предыдущий ответ. Я просто не понимаю ваш вопрос.

Я изменил ваш код следующим образом:

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
0 голосов
/ 27 февраля 2017

Точно не отвечает на вопрос, но вы можете вызывать частные методы таким образом

class Example
 private
 def example_test
  puts 'Hello'
 end
end

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