wake-up-neo.com

Ruby Klasseninstanzvariable vs. Klassenvariable

Ich habe " Wann werden Ruby Instanzvariablen gesetzt? " gelesen, aber ich bin mir nicht sicher, wann ich Klasseninstanzvariablen verwenden soll.

Klassenvariablen werden von allen Objekten einer Klasse gemeinsam genutzt, Instanzvariablen gehören zu einem Objekt. Es gibt nicht mehr viel Platz, um Klasseninstanzvariablen zu verwenden, wenn wir Klassenvariablen haben.

Könnte jemand den Unterschied zwischen diesen beiden erklären und wann man sie benutzt?

Hier ist ein Codebeispiel:

class S
  @@k = 23
  @s = 15
  def self.s
    @s
  end
  def self.k
     @@k
  end

end
p S.s #15
p S.k #23

Ich verstehe jetzt, Klasseninstanzvariablen werden nicht entlang der Vererbungskette weitergegeben!

166
Elmor

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 für eine Klasse (nicht für eine Instanz dieser Klasse) können Sie etwas Gemeinsames für diese Klasse speichern, ohne dass Unterklassen diese automatisch abrufen (und umgekehrt). Mit Klassenvariablen müssen Sie nicht self.class Aus einem Instanzobjekt schreiben, und (falls gewünscht) erhalten Sie auch eine automatische Freigabe in der gesamten Klassenhierarchie.


Kombinieren Sie diese 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] 
250
Phrogz

Ich glaube, der Hauptunterschied ist die Vererbung:

class T < S
end

p T.k
=> 23

S.k = 24
p T.k
=> 24

p T.s
=> nil

Klassenvariablen werden von allen "Klasseninstanzen" (d. H. Unterklassen) gemeinsam genutzt, wohingegen Klasseninstanzvariablen nur für diese Klasse spezifisch sind. Aber wenn Sie nie vorhaben, Ihre Klasse zu erweitern, ist der Unterschied rein akademisch.

30
bioneuralnet

#class-Instanzvariablen sind nur für Klassenmethoden und nicht für Instanzmethoden verfügbar, während Klassenvariablen sowohl für Instanzmethoden als auch für Klassenmethoden verfügbar sind. Auch die Klasseninstanzvariablen gehen in der Vererbungskette verloren, Klassenvariablen hingegen nicht.

class Vars

  @class_ins_var = "class instance variable value"  #class instance variable
  @@class_var = "class variable value" #class  variable

  def self.class_method
    puts @class_ins_var
    puts @@class_var
  end

  def instance_method
    puts @class_ins_var
    puts @@class_var
  end
end

Vars.class_method

puts "see the difference"

obj = Vars.new

obj.instance_method

class VarsChild < Vars


end

VarsChild.class_method
23
Sachin Saxena

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 existiert dieses Verhalten? Nun, alles in Ruby ist ein Objekt - sogar Klassen. Das heißt, jeder Klasse ist ein Objekt der Klasse Class (bzw. einer Unterklasse von Class) zugeordnet. (Wenn Sie class Foo sagen, deklarieren Sie wirklich eine Konstante Foo und weisen ihr ein Klassenobjekt zu.) Und jedes Ruby Objekt kann Instanzvariablen haben, also Klassenobjekte kann auch Instanzvariablen haben.

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 verfügt über ein eigenes Klassenobjekt, und dieses Klassenobjekt verfügt über eigene Instanzvariablen. Deshalb 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.

13

Die Verwendung von Klasseninstanzvariablen mag sofort nützlich erscheinen, da Klasseninstanzvariablen von Unterklassen gemeinsam genutzt werden und sowohl in Singleton- als auch in Instanzmethoden auf sie verwiesen werden kann. Es gibt jedoch einen erheblichen Nachteil. Sie werden gemeinsam genutzt, sodass Unterklassen den Wert der Klasseninstanzvariablen ändern können, und die Basisklasse wird auch von der Änderung betroffen sein, die normalerweise unerwünschtes Verhalten ist:

class C
  @@c = 'c'
  def self.c_val
    @@c
  end
end

C.c_val
 => "c" 

class D < C
end

D.instance_eval do 
  def change_c_val
    @@c = 'd'
  end
end
 => :change_c_val 

D.change_c_val
(irb):12: warning: class variable access from toplevel
 => "d" 

C.c_val
 => "d" 

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 class_attribute-Wert 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 ist jedoch, dass Unterklassen ihren eigenen Wert ändern können und sich nicht auf die übergeordnete Klasse auswirken.

class C
  class_attribute :c
  self.c = 'c'
end

 C.c
 => "c" 

class D < C
end

D.c = 'd'
 => "d" 

 C.c
 => "c" 
0
Donato

Official 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 betrachtet werden, mit all den Problemen, 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:

class Woof

  @@sound = "woof"

  def self.sound
    @@sound
  end
end

Woof.sound  # => "woof"

class LoudWoof < Woof
  @@sound = "WOOF"
end

LoudWoof.sound  # => "WOOF"
Woof.sound      # => "WOOF" (!)

Oder eine Vorfahrenklasse wird später möglicherweise mit überraschenden Effekten wieder geöffnet und geändert:

class Foo

  @@var = "foo"

  def self.var
    @@var
  end
end

Foo.var  # => "foo" (as expected)

class Object
  @@var = "object"
end

Foo.var  # => "object" (!)

Wenn Sie also nicht genau wissen, was Sie tun und dieses Verhalten explizit benötigen, sollten Sie besser Klasseninstanzvariablen verwenden.

0
not a patch