Почему собственный класс не эквивалентен self.class, когда он выглядит так похоже? - PullRequest
79 голосов
/ 27 октября 2009

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

Почему собственный класс объекта отличается от self.class?

class Foo
  def initialize(symbol)
    eigenclass = class << self
      self
    end
    eigenclass.class_eval do
      attr_accessor symbol
    end
  end
end

Моя последовательность логики, которая приравнивает собственный класс к class.self, довольно проста:

class << self - это способ объявления методов класса, а не методов экземпляра. Это ярлык для def Foo.bar.

Таким образом, в ссылке на объект класса возвращение self должно быть идентично self.class. Это потому, что class << self установит self в Foo.class для определения методов / атрибутов класса.

Я просто запутался? Или это хитрая уловка метапрограммирования в Ruby?

Ответы [ 3 ]

120 голосов
/ 27 октября 2009

class << self - это больше, чем просто способ объявления методов класса (хотя его можно использовать и таким образом). Возможно, вы видели какое-то использование вроде:

class Foo
  class << self
    def a
      print "I could also have been defined as def Foo.a."
    end
  end
end

Это работает и эквивалентно def Foo.a, но способ, которым он работает, немного неуловим. Секрет в том, что self в этом контексте относится к объекту Foo, класс которого является уникальным анонимным подклассом Class. Этот подкласс называется Foo s eigenclass . Так def a создает новый метод с именем a в собственном классе Foo, доступный с помощью обычного синтаксиса вызова метода: Foo.a.

Теперь давайте рассмотрим другой пример:

str = "abc"
other_str = "def"

class << str
  def frob
    return self + "d"
  end
end

print str.frob # => "abcd"
print other_str.frob # => raises an exception, 'frob' is not defined on other_str

Этот пример такой же, как и предыдущий, хотя поначалу будет сложно сказать. frob определяется не для класса String, а для собственного класса str, уникального анонимного подкласса String. Таким образом, str имеет метод frob, но экземпляры String, как правило, не имеют. Мы могли бы также переопределить методы String (очень полезно в некоторых сложных тестовых сценариях).

Теперь мы готовы понять ваш оригинальный пример. Внутри метода инициализации Foo, self относится не к классу Foo, а к некоторому конкретному экземпляру из Foo. Его собственный класс является подклассом Foo, но это не Foo; это не могло быть, или иначе уловка, которую мы видели во втором примере, не могла работать. Итак, чтобы продолжить ваш пример:

f1 = Foo.new(:weasels)
f2 = Foo.new(:monkeys)

f1.weasels = 4 # Fine
f2.monkeys = 5 # Also ok
print(f1.monkeys) # Doesn't work, f1 doesn't have a 'monkeys' method.

Надеюсь, это поможет.

45 голосов
/ 16 ноября 2009

Самый простой ответ: собственный класс не может быть создан.

class F
 def eigen
  class << self 
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class
11 голосов
/ 21 декабря 2009

Иегуда Кац довольно хорошо объясняет тонкости в " Метапрограммировании в Ruby: все зависит от самого себя "

...