2014-02-20 8 views
6

ein einfaches Ruby-Programm Angenommen, eine Klasse Variable,Verwirrung über Rubin Klassenvariable

class Holder 
     @@var = 99 
     def Holder.var=(val) 
     @@var = val 
     end 
     def var 
     @@var 
     end 
    end 

    @@var = "top level variable" 

    a = Holder.new 
    puts a.var 

Ich denke, das Ergebnis 99 sein soll, aber der Ausgang ist nicht 99. Ich wundere mich warum. Da der Klassenbereich der Klasse die Klasse ist, nehme ich an, dass die Zeile @@var = "top level variable" die Variable in der Klasse nicht beeinflusst.

Antwort

6

@@var ist eine Klassenvariable Holder. Und die @@var auf der obersten Ebene ist nicht die gleiche Klassenvariable mit dem gleichen Namen @@varHolder, Sie erstellen eine neue Klassenvariable für die Klasse Object. Jetzt wird @@var für die Unterklasse (n) der Elternklasse freigegeben. Object ist eine Elternklasse der Klasse Holder. Wenn Sie in Ruby nicht explizit eine Superklasse einer benutzerdefinierten Klasse definieren, definieren Sie mit dem Schlüsselwort class, und die Klasse Object wird zur Standard-Superklasse der Klasse, die Sie gerade erstellt haben.

Auf der obersten Ebene definieren Sie eine neue Klassenvariable in der Klasse Object, wie Sie es in Ihrem Beispiel getan haben. Wie bei der Vererbung steht es nun der Klasse Holder zur Verfügung.

Ich meine, wenn Sie die unten geschrieben:

class Holder 
    @@var = 99 
    def Holder.var=(val) 
    @@var = val 
    end 
    def var 
    @@var 
    end 
end 

@@var nicht bereits in die Object Klasse hinzugefügt. Jetzt auf der obersten Ebene, wenn Sie schreibt das folgende Zeile:

@@var = "top level variable" 

Es bedeutet, werden Sie es Object Klasse hinzufügen und auch alte Variable (@@var) gleiche Namen wie in der Holder Klasse ein, mit diesem neuen Aktualisierung eine, die Sie gerade im Rahmen der Object Klasse definiert haben.

Denken Sie daran, Klassenvariablen Variablen und sichtbar nur für die Klasse geteilt werden (B), wo man sie und ihre Nachkommen Klasse (C), Nachkomme (D) der abgeleiteten Klasse (C) definiert ist, so weiter. Aber nicht sichtbar durch die Super-Klasse (A wie unten) der Klasse (B wie unten), bis Sie eine gleichnamige Variable in der Elternklasse (A) definiert haben, wie Sie in der Kindklasse haben (B) .

class A 
end 

class B < A 
    @@var = 10 
end 

class C < B 
end 

class D < C 
end 

A.class_variable_get(:@@var) # uninitialized class variable @@var in A (NameError) 
B.class_variable_get(:@@var) # 10 
C.class_variable_get(:@@var) # 10 
D.class_variable_get(:@@var) # 10 
1

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.varnil zurück, weil die Variable für Holder3 durch Vererbung existiert, aber nicht initialisiert wurde.

Der Ausdruck class << self ändert selfHolder2-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 .