ich verstärken möchte, was andere gesagt haben, und insbesondere, zu vergleichen, die Verwendung von Klassenvariablen mit Klasse Instanzvariablen. Beginnen wir mit dieser beginnen:
class Holder1
end
class Holder2 < Holder1
@@var = 99
# Create a class setter and a getter for the class variable
def Holder2.var=(val)
@@var = val
end
def Holder2.var
@@var
end
# Create a instance getter for the class variable
def var
@@var
end
end
class Holder3 < Holder2
end
Holder2.var #=> 99
Holder2.var = 1
Holder3.var #=> 1
Holder1.var #=> NoMethodError: undefined method `var' for Holder1:Class
Holder2.new.var #=> 1
Holder3.new.var #=> 1
Holder3.var = 2
Holder3.var #=> 2
Holder2.var #=> 2
Holder3.new.var #=> 2
Holder2.new.var #=> 2
Abgesehen: Die ersten beiden Methoden in der Regel wie folgt geschrieben werden würde:
def self.var=(val)
@@var = val
end
def self.var
@@var
end
Dies funktioniert, weil self
=>Holder2
wenn die Methoden erstellt werden. Wenn Sie den Klassennamen nicht fest verdrahten, ist keine Änderung erforderlich, wenn Sie die Klasse umbenennen möchten.
Nun kann dies (die Verwendung einer Klassenvariable) genau das Verhalten sein, das Sie wollen.Das heißt, wenn Unterklassen die Variable sehen und in der Lage sein sollen, sie zu ändern, benötigen Sie eine Klassenvariable.
Wenn Sie jedoch jede Unterklasse wollen ihre eigene Variable haben, die weder durch Subklassen noch verändert gesehen werden kann, müssen Sie eine Variable Klasseninstanz verwenden, @var
, anstatt eine Klassenvariable, @@var
. (Technisch ist das nicht ganz richtig, weil man Holder2.instance_variable_get(:@var)
oder Holder2.instance_variable_set(:@var)
überall in Ihrem Programm verwenden könnte.)
Vergleichen Sie die Ergebnisse des Codes, der damit oben folgt. Ich habe eine Instanzvariable mit dem gleichen Namen wie die Klasseninstanzvariable @var
eingefügt, um zu verdeutlichen, dass sie genauso unterschiedlich sind wie @night
und @day
.
class Holder1
end
class Holder2 < Holder1
# Create an accessor for the instance variable
attr_accessor :var
# Initialize class instance variable
@var = 99
# Create an accessor for the class instance variable
class << self
puts "self in 'class << self': #{self}"
attr_accessor :var
def our_dog
"Diva"
end
end
# Create a class method
def self.our_cat
puts "self in 'self.our_cat())' def: #{self}"
"Teagan"
end
# Create an instance setter and a getter for the class instance variable
def c_ivar=(val)
self.class.var = val
end
def c_ivar
self.class.var
end
end
class Holder3 < Holder2
end
#=> self in 'class << self': #<Class:Holder2>
Holder2.var #=> 99
h2 = Holder2.new
h2.var #=> nil
Holder2.var = 1
Holder2.var #=> 1
h2.c_ivar #=> 1
h2.c_ivar = 2
Holder2.var #=> 2
h2.var #=> nil
h2.var = 3
h2.var #=> 3
Holder2.var #=> 2
Holder3.var #=> nil
Holder1.var #=> NoMethodError: undefined method `var' for Holder1:Class
Holder3.var = 4
Holder3.var #=> 4
Holder2.var #=> 2
h3 = Holder3.new
h3.c_ivar #=> 4
h2.c_ivar #=> 2
Holder3.our_dog #=> "Diva"
Holder3.our_cat #=> "self in 'self.our_cat())' def: Holder3"
#=> "Teagan"
Holder1.var
wirft eine Ausnahme, weil diese Klasse keinen Zugriff auf Holder2
‚s Klasseninstanzvariable [email protected]
hat. Im Gegensatz dazu gibt die erste Verwendung von Holder3.var
nil
zurück, weil die Variable für Holder3
durch Vererbung existiert, aber nicht initialisiert wurde.
Der Ausdruck class << self
ändert self
Holder2
-Holder2
‚s Singletonklasse (aka metaclass) bis zum nächsten end
. Beachten Sie, dass Ruby die Singleton-Klasse Holder2
als #<Class:Holder2>
bezeichnet. Außerdem müssen wir my_dog
nicht mit self.
voranstellen, da self
die Singleton-Klasse Holder2
ist, wenn die Methode erstellt wird.
Beachten Sie, dass:
Holder2.singleton_methods #=> [:var, :var=, :our_dog, :our_cat]
Dies zeigt, dass our_cat()
ist eine Methode der Holder2
‚s Singletonklasse, obwohl self
war Holder2
(und nicht Holder2
's Singletonklasse', #<Class:Holder2>
), wenn das Verfahren konstruiert wurde . Aus diesem Grund sagen einige, dass technisch "there is no such thing as a class method". Dies sagt uns auch, dass wir die Definition von my_cat
in das class << self
Konstrukt verschoben haben könnten (und drop self.
).
Weit verbreitet ist auch Instanzvariablen und Methoden zu Holder2
hinzufügen ‚s Singletonklasse ist class << self ... end
mit extend M
zu ersetzen und ein Modul erstellen:
module M
attr_accessor :var
def our_dog
"Diva"
end
end
Object#extend mischt diese Instanzvariablen und Methoden in Holder2
‘ s Singletonklasse .