Unterschied zwischen Klassenvariablen und Klasseninstanzvariablen?

Antworten:

151

Eine Klassenvariable ( @@) wird von der Klasse und allen ihren Nachkommen gemeinsam genutzt. Eine Klasseninstanzvariable ( @) wird von den Nachkommen der Klasse nicht gemeinsam genutzt.


Klassenvariable ( @@)

Lassen Sie uns eine Klasse Foo mit einer Klassenvariablen @@iund Accessoren zum Lesen und Schreiben haben @@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

  def self.i=(value)
    @@i = value
  end

end

Und eine abgeleitete Klasse:

class Bar < Foo
end

Wir sehen, dass Foo und Bar den gleichen Wert haben für @@i:

p Foo.i    # => 1
p Bar.i    # => 1

Und das Ändern @@iin einem ändert es in beiden:

Bar.i = 2
p Foo.i    # => 2
p Bar.i    # => 2

Klasseninstanzvariable ( @)

@iErstellen wir eine einfache Klasse mit einer Klasseninstanzvariablen und Accessoren zum Lesen und Schreiben @i:

class Foo

  @i = 1

  def self.i
    @i
  end

  def self.i=(value)
    @i = value
  end

end

Und eine abgeleitete Klasse:

class Bar < Foo
end

Wir sehen, dass Bar zwar die Accessoren für erbt @i, sich aber nicht @iselbst erbt :

p Foo.i    # => 1
p Bar.i    # => nil

Wir können Bar's setzen, @iohne Foo's zu beeinflussen @i:

Bar.i = 2
p Foo.i    # => 1
p Bar.i    # => 2
Wayne Conrad
quelle
1
Warum Instanzvariablen mit Klassenmethoden zurückgeben? Stoßen Sie oft auf diese Situation?
Sekmo
1
@sekmo Die Accessoren in diesen Beispielen geben entweder Klasseninstanzvariablen zurück , die zur Klasse gehören, oder Klassenvariablen , die zur Klassenhierarchie gehören. Sie geben keine einfachen Instanzvariablen zurück, die zu Instanzen der Klasse gehören. Die Begriffe "Instanzvariable", "Klasseninstanzvariable" und "Klassenvariable" sind ziemlich verwirrend, nicht wahr?
Wayne Conrad
71

Zuerst müssen Sie verstehen, dass Klassen auch Instanzen sind - Instanzen der ClassKlasse.

Sobald Sie das verstanden haben, können Sie verstehen, dass einer Klasse Instanzvariablen zugeordnet werden können, genau wie es einem regulären Objekt (sprich: Nicht-Klasse) möglich ist.

Hello = Class.new

# setting an instance variable on the Hello class
Hello.instance_variable_set(:@var, "good morning!")

# getting an instance variable on the Hello class
Hello.instance_variable_get(:@var) #=> "good morning!"

Beachten Sie, dass eine Instanzvariable ein Hellovöllig unabhängig von einer Instanzvariablen in einer Instanz von ist und sich von dieser unterscheidetHello

hello = Hello.new

# setting an instance variable on an instance of Hello
hello.instance_variable_set(:@var, :"bad evening!")

# getting an instance variable on an instance of Hello
hello.instance_variable_get(:@var) #=> "bad evening!")

# see that it's distinct from @var on Hello
Hello.instance_variable_get(:@var) #=> "good morning!"

Eine Klassenvariable ist andererseits eine Art Kombination der beiden oben genannten, da sie auf sich Helloselbst und ihre Instanzen sowie auf Unterklassen Hellound deren Instanzen zugänglich ist :

HelloChild = Class.new(Hello)
Hello.class_variable_set(:@@class_var, "strange day!")
hello = Hello.new
hello_child = HelloChild.new

Hello.class_variable_get(:@@class_var) #=> "strange day!"
HelloChild.class_variable_get(:@@class_var) #=> "strange day!"
hello.singleton_class.class_variable_get(:@@class_var) #=> "strange day!"
hello_child.singleton_class.class_variable_get(:@@class_Var) #=> "strange day!"

Viele Leute sagen, class variableswegen des seltsamen Verhaltens oben zu vermeiden , und empfehlen die Verwendung von class instance variablesstattdessen.

horseyguy
quelle
2
+1 Super Erklärung! Dies sollte jeder Programmierer, der neu bei Ruby ist, verdauen.
TomZ
0

Außerdem möchte ich hinzufügen, dass Sie @@von jeder Instanz der Klasse aus Zugriff auf die Klassenvariable ( ) erhalten können

class Foo
  def set_name
    @@name = 'Nik'
  end

  def get_name
    @@name
  end
end


a = Foo.new
a.set_name
p a.get_name # => Nik
b = Foo.new
p b.get_name # => Nik

Sie können jedoch nicht dasselbe für die Klasseninstanzvariable ( @) tun

class Foo
  def set_name
    @name = 'Nik'
  end

  def get_name
    @name
  end
end


a = Foo.new
a.set_name
p a.get_name # => Nik
b = Foo.new
p b.get_name # => nil
Evan Ross
quelle
-1: Dies ist falsch: Auf Klasseninstanzvariablen kann von jeder Instanz dieser Klasse aus zugegriffen werden (vorausgesetzt, die Accessoren sind vorhanden). Außerdem zeigt Ihr Beispiel reguläre Instanzvariablen, keine Klasseninstanzvariablen. Klasseninstanzvariablen werden außerhalb von Methoden deklariert und unterscheiden sich grundlegend von regulären Instanzvariablen und Klassenvariablen.
Der Pellmeister
Wie können Sie von jeder Instanz dieser Klasse auf Klasseninstanzvariablen zugreifen?
Evan Ross
Hast du Wayne Conrads Antwort gelesen? Das erklärt es buchstäblich. stackoverflow.com/a/3803089/439592
Der Pellmeister
Ich weiß, dass Sie Zugriff von Klassenmethoden wie erhalten können Foo.i, aber wie können Sie dasselbe für jede Instanz dieser Klasse wie tun Foo.new.i?
Evan Ross
Mit #class. Ich persönlich denke, dass #classVerwendungen, wenn sie auf etwas aufgerufen werden, aber selfCode-Gerüche sind. Sie können sogar noch einen Schritt weiter gehen und die Instanz Accessoren implementieren iund i=dass die Delegierten ihre #classÄquivalente, wobei in diesem Fall die Sie tun können Foo.new.i. Ich empfehle dies nicht, da dadurch eine verwirrende Oberfläche entsteht, die darauf hindeutet, dass Sie ein Objektmitglied ändern.
Der Pellmeister