Ruby define_method вопрос - PullRequest
3 голосов
/ 30 июля 2010

Я читаю Метапрограммирование в Ruby.

Вот два фрагмента кода из книги:

my_var = "Success"

MyClass = Class.new do 
  puts "#{my_var} in the class definition!"
  define_method :my_method do
    puts "#{my_var} in the method!"
  end
end

MyClass.new.my_method

⇒ Success in the class definition!
  Success in the method!

и

def define_methods
  shared = 0

  Kernel.send :define_method, :counter do
    shared
  end

  Kernel.send :define_method, :inc do |x|
    shared += x
  end
end

define_methods

counter # => 0 
inc(4) 
counter # => 4

Интересно, почему не нужно использовать динамическую диспетчеризацию (использование Kernel.send) при определении метода в первом примере, а нужно использовать его во втором примере.

Я подумал, но не могу понять.

Спасибо!

Ответы [ 2 ]

5 голосов
/ 30 июля 2010

Module#define_method является приватным.Частные методы могут быть вызваны только без получателя.Итак, во втором примере вам нужно использовать отражение (т.е. send), чтобы обойти ограничения доступа.

Обратите внимание, что в первом примере все еще используется динамическая диспетчеризация.Фактически, в Ruby все (кроме доступа к переменной и некоторых встроенных операторов) использует динамическую диспетчеризацию.

1 голос
/ 03 апреля 2013

Вопреки тому, что говорится в книге, она работает без Kernel.send, вам просто нужен контекст, в котором класс для self включает Kernel - что почти всегда так.

например, если вы сделаете это в основной области видимости, это будет там:

irb(main):024:0> self.class.ancestors
=> [Object, Kernel, BasicObject]

Итак, используя

  define_method :counter do
    shared
  end

отлично работает.

Вам нужен только этот трюк, если в текущем контексте Kernel не включен, например если вы находитесь внутри простого BasicObject или какого-то созданного пользователем класса-потомка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...