2009-10-27 11 views
74

Ich habe das Memo irgendwo vergessen, und ich hoffe, Sie erklären es mir.Warum ist die Eigenklasse nicht äquivalent zu self.class, wenn sie so ähnlich aussieht?

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 Zug von Logik, die die Eigenklasse mit class.self entspricht ist ziemlich einfach:

class << self ist eine Möglichkeit, Klassenmethoden zu erklären, anstatt Instanzmethoden. Es ist eine Verknüpfung zu def Foo.bar.

In der Referenz auf das Klassenobjekt sollte die Rückgabe self also identisch mit self.class sein. Dies liegt daran, dass class << selfself auf Foo.class für die Definition von Klassenmethoden/-attributen festlegen würde.

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

Antwort

106

class << self ist mehr als nur eine Art, Klassenmethoden zu deklarieren (obwohl es auf diese Weise verwendet werden kann). Wahrscheinlich haben Sie einige Nutzung wie gesehen:

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

Das funktioniert und ist äquivalent zu def Foo.a, aber die Art und Weise funktioniert es ist ein wenig subtiler. Das Geheimnis ist, dass sich self in diesem Kontext auf das Objekt Foo bezieht, dessen Klasse eine eindeutige anonyme Unterklasse von Class ist. Diese Unterklasse wird Foo 's Eigenklasse genannt. So def a erstellt eine neue Methode namens a in Foo 's Eigenklasse, zugänglich durch die normale Methode Aufrufsyntax: Foo.a.

Jetzt ist an einem anderen Beispiel aussehen lassen:

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 die letzten, obwohl es schwierig sein kann, zunächst zu erzählen. frob ist definiert, nicht auf die String Klasse, sondern auf die Eigenklasse str, eine einzigartige anonyme Unterklasse von String. So str hat eine frob Methode, aber Instanzen von String im Allgemeinen nicht. Wir könnten auch Methoden von String überschrieben haben (sehr nützlich in bestimmten schwierigen Testszenarien).

Jetzt sind wir bereit, Ihr ursprüngliches Beispiel zu verstehen. In der Foo Initialisierungsmethode bezieht sich self nicht auf die Klasse Foo, sondern auf eine bestimmte Instanz von Foo. Seine Eigenklasse ist eine Unterklasse von Foo, aber sie ist nicht Foo; es könnte nicht sein, sonst könnte der Trick, den wir im zweiten Beispiel sahen, nicht funktionieren. So 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. 

Hoffen, dass dies hilft.

+0

So dann jede Instanz ist eine anonyme Unterklasse der Klasse erstellt? –

+19

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. –

+4

nette Antwort :) viele Leute verstehen das nicht so klar wie Sie. – horseyguy

43

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 
+0

Sie können nur einen Punkt auf dieser Website haben, aber ich mag Sie und Ihren Stil. – horseyguy

+0

Zustimmen mit Geländer; das ist eine tolle Antwort –

+1

Interessante Informationen, aber beantworten Sie die Frage nicht – Alexey