define_method и класс объекта: странное поведение


РЕДАКТИРОВАТЬ: Позвольте мне немного рассказать о моих намерениях:

При условии, что объект ruby ​​получает свои методы из методов экземпляра своего класса.

Я пытался «доказать» то, что, добавляя методы экземпляра к классу этого объекта, они затем станут методами для самого объекта.

So:

class Test;end
Test.class.class_eval do 
  def foo
    puts "I am a method in #{self}"
  end
end
Test.foo
=> I am a method in Test
Test.class.foo
=> I am a method in Class

Я вообще-то уже разобрался, почему это происходит. Это потому, что Class.class - это ... Класс! Поскольку Class имеет foo как часть своей таблицы методов экземпляра, и поскольку Class.class указывает на себя, он также будет иметь foo как вызываемый метод экземпляра.

Исходный пост следует:

Просто ради того, чтобы больше узнать об объектной модели Ruby, я провел несколько экспериментов, и следующее поведение меня удивило:

Допустим, у нас есть класс Test:

class Test;end

Затем мы class_eval Test.class, чтобы добавить метод к объекту Test:

Test.class.class_eval do
  def foo 
    puts 'foo called!'
  end
end

(эквивалентно вызову Test.class.send (: define_method,: foo) ...)

Потом:

irb(main):076:0> Test.foo
=> foo called!

Но отправка foo в класс также работает:

irb(main):006:0> Test.class.foo
=> foo called!

Однако я до сих пор не могу понять, почему. Singleton_class класса не содержит его (единственное возможное объяснение, которое пришло мне в голову, заключалось в том, что каким-то образом метод был добавлен в одноэлементный класс класса):

Test.class.singleton_methods
=> [:nesting, :constants]

Как в этом случае работает поиск метода? Почему отправка: foo в Test.class также вызывает метод? Я что-то упускаю ?


В качестве справки я проделал то же самое с экземпляром Test:

irb(main):001:0> class Test;end
=> nil
irb(main):002:0> _foo = Test.new
=> #<Test:0x007fa2c39e2f38>
irb(main):003:0> _foo.class.class_eval do
irb(main):004:1*   def foo
irb(main):005:2>    puts 'foo called!'
irb(main):006:2>   end
irb(main):007:1> end
=> :foo
irb(main):008:0> _foo.foo
foo called!
=> nil
irb(main):009:0> _foo.class.foo
NoMethodError: undefined method `foo' for Test:Class

Это сработало так, как я ожидал.

Заранее спасибо!


person FullOfCaffeine    schedule 29.01.2014    source источник


Ответы (3)


Когда вы вызываете Test.class, он возвращает класс Test не фактического класса Test.

irb(main):001:0> class Test;end
=> nil
irb(main):002:0> Test.class
=> Class

Когда вы вызываете _foo = Test.new, вы захватываете экземпляр класса, а затем, когда вы вызываете _foo.class, он возвращает класс экземпляра foo, который равен Test

В первом примере вы добавляете foo в суперкласс, а не в класс Test.

Чтобы подробнее рассказать об этом, вам следует вызвать class_eval в классе Test. Кроме того, вам нужен метод класса, а не метод экземпляра. Поскольку класс Test на самом деле является экземпляром Class, вы фактически определили метод экземпляра в классе Class вместо определения метода класса в Test. Что вам следует сделать, это следующее:

Test.class_eval do
  def self.foo
    puts "foo called!" 
  end
end
person snowe    schedule 29.01.2014
comment
Хороший ответ. Есть простая проверка вашего утверждения: Class.foo => foo called! - person Cary Swoveland; 29.01.2014
comment
Привет. Спасибо, что нашли время ответить. На самом деле я не пытался добиться чего-то особенного. Мне было интересно, почему отправка сообщения: foo в Test.class вызвала метод foo. Я уже разобрался и отредактировал исходный пост. - person FullOfCaffeine; 29.01.2014
comment
Так я ответил на твой вопрос? Остались вопросы? Не могли бы вы уточнить пожалуйста. - person snowe; 29.01.2014
comment
В своем редактировании вы действительно ошибаетесь. Это потому, что Test.class Class. Вы определяете метод экземпляра для объекта Class, и поскольку ваш класс Test является экземпляром Class, он имеет этот метод. - person snowe; 29.01.2014
comment
Почему не так? Я знаю, что Test.class - это Class. Но потом я сказал Class.class == Class. В итоге: потому что Test.class == Class и Class.class == Class. Так что да, во всем разобрался. Я закрою. - person FullOfCaffeine; 29.01.2014

Вы немного перепутали.

  • Test.class_eval выполняется на собственном классе Test (это Test.)
  • Test.class.class_eval выполняется на собственном классе Test собственного класса (который, по-видимому, равен Class.)
  • Test.class.singleton_methods возвращает методы Class ’собственного класса.

Test.class.class_eval фактически выполняет код на Class (в вашем случае объявляя метод экземпляра Class ’).

Test.foo называется без проблем, потому что вы объявили #foo на Class, а Test является его экземпляром (а также Test.class.)

person Aleksei Matiushkin    schedule 29.01.2014
comment
Привет. Отправка: foo в Test.class работает, потому что Test.class.class - это Class. Это собственно ответ на мой вопрос. Спасибо, что нашли время ответить. - person FullOfCaffeine; 29.01.2014

Короче говоря, отправка foo в Test.class работает, потому что:

Test.class == Class and Class.class == Class

Class.class указывает на себя.

person FullOfCaffeine    schedule 29.01.2014