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?
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 Foo
classe 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.
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
Yehuda Katz explique assez bien les subtilités de " Métaprogrammation en Ruby: Tout est question de soi "