Instanzvariable für eine Klasse:
class Parent
@things = []
def self.things
@things
end
def things
self.class.things
end
end
class Child < Parent
@things = []
end
Parent.things << :car
Child.things << :doll
mom = Parent.new
dad = Parent.new
p Parent.things #=> [:car]
p Child.things #=> [:doll]
p mom.things #=> [:car]
p dad.things #=> [:car]
Klassenvariable:
class Parent
@@things = []
def self.things
@@things
end
def things
@@things
end
end
class Child < Parent
end
Parent.things << :car
Child.things << :doll
p Parent.things #=> [:car,:doll]
p Child.things #=> [:car,:doll]
mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new
[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
Mit einer Instanzvariablen in einer Klasse (nicht in einer Instanz dieser Klasse) können Sie etwas speichern, das dieser Klasse gemeinsam ist, ohne dass Unterklassen diese automatisch auch abrufen (und umgekehrt). Mit Klassenvariablen haben Sie die Möglichkeit, nicht self.class
aus einem Instanzobjekt schreiben zu müssen , und (falls gewünscht) erhalten Sie auch eine automatische Freigabe in der gesamten Klassenhierarchie.
Zusammenführen dieser Elemente zu einem einzigen Beispiel, das auch Instanzvariablen für Instanzen abdeckt:
class Parent
@@family_things = [] # Shared between class and subclasses
@shared_things = [] # Specific to this class
def self.family_things
@@family_things
end
def self.shared_things
@shared_things
end
attr_accessor :my_things
def initialize
@my_things = [] # Just for me
end
def family_things
self.class.family_things
end
def shared_things
self.class.shared_things
end
end
class Child < Parent
@shared_things = []
end
Und dann in Aktion:
mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new
Parent.family_things << :house
papa.family_things << :vacuum
mama.shared_things << :car
papa.shared_things << :blender
papa.my_things << :quadcopter
joey.my_things << :bike
suzy.my_things << :doll
joey.shared_things << :puzzle
suzy.shared_things << :blocks
p Parent.family_things #=> [:house, :vacuum]
p Child.family_things #=> [:house, :vacuum]
p papa.family_things #=> [:house, :vacuum]
p mama.family_things #=> [:house, :vacuum]
p joey.family_things #=> [:house, :vacuum]
p suzy.family_things #=> [:house, :vacuum]
p Parent.shared_things #=> [:car, :blender]
p papa.shared_things #=> [:car, :blender]
p mama.shared_things #=> [:car, :blender]
p Child.shared_things #=> [:puzzle, :blocks]
p joey.shared_things #=> [:puzzle, :blocks]
p suzy.shared_things #=> [:puzzle, :blocks]
p papa.my_things #=> [:quadcopter]
p mama.my_things #=> []
p joey.my_things #=> [:bike]
p suzy.my_things #=> [:doll]
self.things
auf eine Methodethings
im aktuellen Bereich verwiesen (im Fall einer Instanz einer Klasse ist dies die Methode der Instanz), wobeiself.class.things
auf einethings
Methode aus der Klasse des aktuellen Bereichs verwiesen wird (wiederum im Fall einer Instanz einer Klasse würde dies bedeuten die Klassenmethode).Ich glaube, der Hauptunterschied (nur?) Ist die Vererbung:
Klassenvariablen werden von allen "Klasseninstanzen" (dh Unterklassen) gemeinsam genutzt, während Klasseninstanzvariablen nur für diese Klasse spezifisch sind. Wenn Sie jedoch niemals beabsichtigen, Ihre Klasse zu erweitern, ist der Unterschied rein akademisch.
quelle
S.new.s => nil
undS.new.k => 23
.Quelle
Verfügbarkeit für Instanzmethoden
Vererbbarkeit
quelle
Wie bereits erwähnt, werden Klassenvariablen von einer bestimmten Klasse und ihren Unterklassen gemeinsam genutzt. Klasseninstanzvariablen gehören zu genau einer Klasse. seine Unterklassen sind getrennt.
Warum gibt es dieses Verhalten? Nun, alles in Ruby ist ein Objekt - sogar Klassen. Das bedeutet, dass jeder Klasse ein Objekt der Klasse
Class
(oder vielmehr eine Unterklasse vonClass
) entspricht. (Wenn Sie sagenclass Foo
, deklarieren Sie wirklich eine KonstanteFoo
und weisen ihr ein Klassenobjekt zu.) Und jedes Ruby-Objekt kann Instanzvariablen haben, sodass Klassenobjekte auch Instanzvariablen haben können.Das Problem ist, dass sich Instanzvariablen für Klassenobjekte nicht so verhalten, wie Sie es normalerweise von Klassenvariablen erwarten. Normalerweise möchten Sie, dass eine in einer Oberklasse definierte Klassenvariable mit ihren Unterklassen geteilt wird, aber so funktionieren Instanzvariablen nicht - die Unterklasse hat ein eigenes Klassenobjekt, und dieses Klassenobjekt hat seine eigenen Instanzvariablen. Daher haben sie separate Klassenvariablen mit dem Verhalten eingeführt, das Sie eher wollen.
Mit anderen Worten, Klasseninstanzvariablen sind eine Art Unfall von Rubys Design. Sie sollten sie wahrscheinlich nicht verwenden, es sei denn, Sie wissen genau, dass sie das sind, wonach Sie suchen.
quelle
Offizielle Ruby-FAQ: Was ist der Unterschied zwischen Klassenvariablen und Klasseninstanzvariablen?
Der Hauptunterschied ist das Verhalten in Bezug auf die Vererbung: Klassenvariablen werden von einer Klasse und allen ihren Unterklassen gemeinsam genutzt, während Klasseninstanzvariablen nur zu einer bestimmten Klasse gehören.
Klassenvariablen können in gewisser Weise als globale Variablen im Kontext einer Vererbungshierarchie mit allen Problemen angesehen werden, die mit globalen Variablen verbunden sind. Beispielsweise kann eine Klassenvariable (versehentlich) von einer ihrer Unterklassen neu zugewiesen werden, was sich auf alle anderen Klassen auswirkt:
Oder eine Ahnenklasse wird später wieder geöffnet und geändert, mit möglicherweise überraschenden Auswirkungen:
Wenn Sie also nicht genau wissen, was Sie tun, und diese Art von Verhalten explizit benötigen, sollten Sie besser Klasseninstanzvariablen verwenden.
quelle
Für diejenigen mit einem C ++ - Hintergrund könnte ein Vergleich mit dem C ++ - Äquivalent von Interesse sein:
Wie wir sehen können,
k
ist einestatic
ähnliche Variable. Das ist 100% wie eine globale Variable, mit der Ausnahme , dass es im Besitz von der Klasse ( scoped korrekt zu sein). Dies erleichtert das Vermeiden von Konflikten zwischen ähnlich benannten Variablen. Wie bei jeder globalen Variablen gibt es nur eine Instanz dieser Variablen, und das Ändern ist immer für alle sichtbar.Andererseits
s
ist ein objektspezifischer Wert. Jedes Objekt hat eine eigene Instanz des Werts. In C ++ müssen Sie eine Instanz erstellen, um auf diese Variable zugreifen zu können. In Ruby ist die Klassendefinition selbst eine Instanz der Klasse (in JavaScript wird dies als Prototyp bezeichnet), daher können Sies
ohne zusätzliche Instanziierung von der Klasse aus darauf zugreifen . Die Klasseninstanz kann geändert werden, die Änderung vons
ist jedoch für jede Instanz (jedes Objekt vom TypS
) spezifisch . Wenn Sie also einen ändern, ändert sich der Wert in einem anderen nicht.quelle
Während es sofort nützlich erscheinen mag, Klasseninstanzvariablen zu verwenden, da Klasseninstanzvariablen von Unterklassen gemeinsam genutzt werden und sowohl in Singleton- als auch in Instanzmethoden verwendet werden können, gibt es einen wesentlichen Nachteil. Sie werden gemeinsam genutzt, sodass Unterklassen den Wert der Klasseninstanzvariablen ändern können. Die Basisklasse ist auch von der Änderung betroffen, die normalerweise unerwünscht ist:
Rails führt eine praktische Methode namens class_attribute ein. Wie der Name schon sagt, deklariert es ein Attribut auf Klassenebene, dessen Wert von Unterklassen vererbt werden kann. Auf den Wert class_attribute kann sowohl in Singleton- als auch in Instanzmethoden zugegriffen werden, wie dies bei der Klasseninstanzvariablen der Fall ist. Der große Vorteil von class_attribute in Rails besteht jedoch darin, dass Unterklassen ihren eigenen Wert ändern können und sich nicht auf die übergeordnete Klasse auswirken.
quelle
self.
jedes Mal, wenn Sie auf das Attribut zugreifen möchten, ein Präfix vorangestellt wirdc
, zself.c
. Die Dokumente sagen, dass eindefault:
Parameter übergeben werden kann,class_attribute
aber es scheint aufgrund des Punktes, mit dem ich gerade erwähnt habe, nicht zu funktionierenself
.