Какова связь между метаклассом Base и производным классом в Ruby? - PullRequest
6 голосов
/ 26 января 2010

В Ruby мы можем использовать super в методе singleton для вызова метода singleton соответствующего суперкласса, как показано в следующем коде.

class Base
  def self.class_method
    puts "Base class method"
  end
end

class Derived < Base
  def self.class_method
    puts "Derived class method"
    super
  end
end

Derived.class_method
# Derived class method
# Base class method

Однако я не совсем понимаю, как этот звонок на super в пределах Derived.class_method может достичь Base.class_method. Я бы предположил, что class_method определено в их метаклассе, означает ли это, что их метакласс имеет отношения родитель / потомок? (Не могу подтвердить это экспериментами)

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

Ответы [ 3 ]

11 голосов
/ 26 января 2010

Здесь в игре четыре объекта класса:

<Class>---class---><Class>
Base               #Base
   ^                  ^
   |                  |
   |                  |
 super              super
   |                  |
   |                  |
<Class>            <Class>
Derived---class--->#Derived

Номенклатура:

  • <...> - это класс каждого объекта.
  • Имя класса находится во второй строке.
  • Если имя начинается с #, это собственный класс (он же синглтон-класс).
  • супер указывает на суперкласс класса
  • класс указывает на класс класса.

Когда вы вызываете Derived.class_method, Ruby следует правилу «правильный, потом вверх»: сначала перейдите к классу объекта, затем следуйте по цепочке суперклассов, останавливаясь, когда метод найден:

  • Получатель вызова "class_method" является производным. Поэтому следуйте по цепочке справа к объекту класса Derived, который является его собственным классом (#Derived).
  • Derived не определяет метод, поэтому Ruby следует по цепочке вверх по цепочке до суперкласса # Derived, который является # Base.

  • Метод найден там, поэтому Ruby отправляет сообщение в # Base.class_method

Вы не думаете, что я знал все эти вещи с головы до головы, не так ли? Вот где мой мозг получил всю эту мета-джуджу: Метапрограммирование Ruby .

Часть 2. Как заставить "собственный класс" (он же "синглтон-класс") выйти из укрытия

class Object
  def eigenclass
    class << self
      self
    end
  end
end

Этот метод вернет собственный класс любого объекта. А как насчет классов? Это тоже объекты.

p Derived.eigenclass               # => #<Class:Derived>
p Derived.eigenclass.superclass    # => #<Class:Base>
p Base.eigenclass                  # => #<Class:Base>

Примечание: выше от Ruby1.9. При запуске под Ruby 1.8 вы получаете сюрприз:

p Derived.eigenclass.superclass    # => #<Class:Class>
4 голосов
/ 26 января 2010

Чтобы уточнить и исправить то, что я написал в комментариях относительно того, как Ruby скрывает / выставляет собственные классы, вот ситуация:

Ruby 1.8:

(1) Метод Object#class всегда возвращает первый реальный (не собственный класс или класс iClass) суперкласса фактического класса объекта. например,

o = Object.new
class << o; end
o.class #=> returns Object, even though the _actual_ class is the eigenclass of o

Другими словами, метод Object#class никогда не будет возвращать собственный класс, он проходит над ними и вместо этого возвращает первый «реальный» класс, который он находит в иерархии наследования.

(2) Метод Class#superclass имеет два случая. Если получатель не является собственным классом, он просто возвращает суперкласс. Однако, если получатель является собственным классом, тогда этот метод возвращает фактический (т.е. не обязательно действительный) класс получателя.

# case where receiver is a normal class (i.e not an eigenclass)
Module.superclass #=> Object (behaves as expected)

# case where receiver is an eigenclass
class << Module; superclass; end #=> returns Class, this is NOT the superclass

Сверху, Class#superclass ведет себя как и ожидалось в случае нормального класса, но для примера с собственным классом в нем говорится, что суперклассом собственного класса модуля является класс, который не соответствует действительности. Из этой диаграммы http://banisterfiend.wordpress.com/2008/10/25/the-secret-life-of-singletons/ мы знаем, что суперкласс собственного класса модуля на самом деле является собственным классом объекта. Я не уверен, почему в Ruby 1.8 такое странное поведение.

Ruby 1.9:

(1) Метод Object#class ведет себя идентично версии 1.8.

(2) У метода Class#superclass больше нет двух случаев, теперь он обрабатывает собственные классы так же, как он обрабатывает обычные классы и возвращает фактический суперкласс, как и ожидалось.

1035 * например *

class << Module; superclass; end #=> #<Class:Object>
3 голосов
/ 26 января 2010

Эта диаграмма объясняет взаимосвязь: http://banisterfiend.wordpress.com/2008/11/25/a-complete-ruby-class-diagram/

Кроме того, вот некоторые другие сообщения, которые объясняют больше тонкостей собственных классов: http://www.klankboomklang.com/2007/10/05/the-metaclass/

http://www.klankboomklang.com/2007/09/21/the-singleton-class/

А вот довольно трудолюбивый, который объясняет больше, чем вы, вероятно, хотели бы знать: http://banisterfiend.wordpress.com/2008/10/25/the-secret-life-of-singletons/

...