Warum ist die Eigenklasse nicht gleichbedeutend mit self.class, wenn sie so ähnlich aussieht?

83

Ich habe das Memo irgendwo verpasst und hoffe, dass Sie mir das erklären.

Warum unterscheidet sich die Eigenklasse eines Objekts von self.class?

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

Mein Logikzug, mit dem die Eigenklasse gleichgesetzt wird, class.selfist ziemlich einfach:

class << selfist eine Möglichkeit, Klassenmethoden anstelle von Instanzmethoden zu deklarieren. Es ist eine Abkürzung zu def Foo.bar.

Innerhalb des Verweises auf das Klassenobjekt selfsollte die Rückgabe also identisch sein mit self.class. Dies liegt daran class << selfsetzen würde , selfum Foo.classfür die Definition von Klassenmethoden / Attribute.

Bin ich nur verwirrt Oder ist das ein hinterhältiger Trick der Ruby-Metaprogrammierung?

Robert K.
quelle

Antworten:

122

class << selfist mehr als nur eine Möglichkeit, Klassenmethoden zu deklarieren (obwohl es auf diese Weise verwendet werden kann). Wahrscheinlich haben Sie eine Verwendung gesehen wie:

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

Dies funktioniert und ist gleichbedeutend mit def Foo.a, aber die Art und Weise, wie es funktioniert, ist ein wenig subtil. Das Geheimnis ist, dass es selfsich in diesem Zusammenhang auf das Objekt bezieht Foo, dessen Klasse eine eindeutige, anonyme Unterklasse von ist Class. Diese Unterklasse heißt Foo‚s Eigenklasse . Erstellt also def aeine neue Methode, die ain Fooder Eigenklasse aufgerufen wird und auf die über die normale Methodenaufrufsyntax zugegriffen werden kann : Foo.a.

Schauen wir uns nun ein anderes Beispiel an:

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

Dieses Beispiel ist das gleiche wie das letzte, obwohl es zunächst schwer zu sagen sein kann. frobwird nicht für die StringKlasse, sondern für die Eigenklasse von streiner eindeutigen anonymen Unterklasse von definiert String. So strhat eine frobMethode, aber Instanzen von Stringim Allgemeinen nicht. Wir könnten auch Methoden von String überschrieben haben (sehr nützlich in bestimmten kniffligen Testszenarien).

Jetzt können wir Ihr ursprüngliches Beispiel verstehen. FooDie Initialisierungsmethode von Inside selfbezieht sich nicht auf die Klasse Foo, sondern auf eine bestimmte Instanz von Foo. Seine Eigenklasse ist eine Unterklasse von Foo, aber es ist nicht Foo; es könnte nicht sein, sonst könnte der Trick, den wir im zweiten Beispiel gesehen haben, nicht funktionieren. Um Ihr Beispiel fortzusetzen:

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.

Hoffe das hilft.

David Seiler
quelle
Jede Instanz ist also eine anonyme Unterklasse der erstellten Klasse?
Robert K
21
Die Klasse jeder Instanz ist eine anonyme Unterklasse der erstellten Klasse. Die Klasse von f1 ist eine anonyme Unterklasse von Foo, die Klasse von Foo ist eine anonyme Unterklasse von Class.
David Seiler
6
nette Antwort :) Viele Leute verstehen das nicht so klar wie Sie.
Horseyguy
3
Wie unterscheidet sich die Eigenklasse von f1 konzeptionell von der tatsächlichen Instanz von f1? Wenn f1 die einzige Instanz ist, die jemals Zugriff auf die Methoden ihrer Eigenklasse haben wird, bricht dann nicht die Unterscheidung zwischen f1 und seiner Eigenklasse zusammen?
Elju
1
@elju Ja, irgendwie. Der wirklich wichtige Unterschied besteht zwischen "Foo" und "f1s Eigenklasse"; Wenn du das hast, geht es dir wahrscheinlich gut.
David Seiler
46

Die einfachste Antwort: Die Eigenklasse kann nicht instanziiert werden.

class F
 def eigen
  class << self 
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class
b.vandgrift
quelle
Sie haben vielleicht nur 1 Punkt auf dieser Website, aber ich mag Sie und Ihren Stil.
Horseyguy
Zustimmen mit Geländer; Dies ist eine großartige Antwort
Christopher Scott
3
Dies ist ein äußerst aufschlussreicher und hilfreicher Kommentar, wenn man die Antwort von @ DavidSeiler oben bereits gelesen hat.
Jazz
Die Macht hier demonstriert die Ausnahme, die ausgelöst wird.
New Alexandria