86 votes

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

J'ai raté le mémo quelque part, et j'espère que vous me l'expliquerez.

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

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

Mon raisonnement qui assimile la classe propre à la classe.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 pour def Foo.bar .

Ainsi, dans la référence à l'objet classe, le retour de self devrait être identique à self.class . Cela s'explique par le fait que class << self mettrait le self en Foo.class pour la définition des méthodes/attributs de classe.

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

124voto

David Seiler Points 6212

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

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

Cela fonctionne, et est équivalent à def Foo.a mais la façon dont cela fonctionne est un peu plus 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 Foo 's Classe propre . Donc def a crée une nouvelle méthode appelée a dans Foo accessible par la syntaxe normale d'appel de méthode : 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 précédent, bien que cela puisse être difficile à voir au premier abord. frob est défini, et non sur le String mais sur la classe propre de str une sous-classe anonyme unique de String . Donc str a un frob mais les instances de la méthode String en général ne le font pas. Nous pourrions également avoir des méthodes surchargées de String (très utile dans certains scénarios de test délicats).

Maintenant, nous sommes équipés pour comprendre votre exemple original. A l'intérieur de Foo de la méthode d'initialisation, self ne fait pas référence à la classe Foo mais à un certain niveau instance de Foo . Sa classe propre est une sous-classe de Foo mais ce n'est pas le cas Foo ; ce n'est pas possible, sinon l'astuce que nous avons vue dans le deuxième exemple ne pourrait 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 vous aidera.

50voto

b.vandgrift Points 261

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

11voto

Pete Hodgson Points 5147

Yehuda Katz fait un bon travail d'explication des subtilités en cet article de blog

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X