web-dev-qa-db-fra.com

Pourquoi la classe propre n'est-elle pas équivalente à self.class, alors qu'elle semble si similaire?

J'ai manqué le mémo quelque part, et j'espère que vous m'expliquerez cela.

Pourquoi la classe propre d'un objet est-elle différente de self.class?

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

Mon train de logique qui assimile la classe propre avec class.self est assez simple:

class << self est un moyen de déclarer des méthodes de classe, plutôt que des méthodes d'instance. C'est un raccourci vers def Foo.bar.

Ainsi, dans la référence à l'objet classe, le retour de self doit être identique à self.class. Ceci est dû au fait class << self définirait self sur Foo.class pour la définition des méthodes/attributs de classe.

Suis-je juste confus? Ou est-ce une astuce sournoise de la méta-programmation Ruby?

82
Robert K

class << self est plus qu'une simple façon de déclarer des méthodes de classe (bien qu'il puisse être utilisé de cette façon). Vous avez probablement vu une utilisation comme:

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

Cela fonctionne et équivaut à def Foo.a, mais la façon dont cela fonctionne est un peu subtile. Le secret est que self, dans ce contexte, fait référence à l'objet Foo, dont la classe est une sous-classe unique et anonyme de Class. Cette sous-classe est appelée Fooclasse propre. Alors def a crée une nouvelle méthode appelée a dans la classe propre de Foo, accessible par la syntaxe d'appel de méthode normale: Foo.a.

Voyons maintenant un autre exemple:

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

Cet exemple est le même que le dernier, bien qu'il puisse être difficile à dire au début. frob est défini, non pas sur la classe String, mais sur la classe propre de str, une sous-classe anonyme unique de String. Ainsi, str possède une méthode frob, mais pas les instances de String en général. Nous pourrions également remplacer les méthodes de String (très utiles dans certains scénarios de test délicats).

Nous sommes maintenant équipés pour comprendre votre exemple original. Dans la méthode d'initialisation de Foo, self ne fait pas référence à la classe Foo, mais à une certaine instance de Foo. Sa classe propre est une sous-classe de Foo, mais ce n'est pas Foo; cela ne pouvait pas être le cas, sinon l'astuce que nous avons vue dans le deuxième exemple ne pouvait pas fonctionner. Donc, pour continuer votre exemple:

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.

J'espère que cela t'aides.

121
David Seiler

La réponse la plus simple: la classe propre ne peut pas être instanciée.

class F
 def eigen
  class << self 
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class
46
b.vandgrift

Yehuda Katz explique assez bien les subtilités de " Métaprogrammation en Ruby: Tout est question de soi "

11
Pete Hodgson