Différence entre les variables de classe et les variables d'instance de classe?

Réponses:

151

Une variable de classe ( @@) est partagée entre la classe et tous ses descendants. Une variable d'instance de classe ( @) n'est pas partagée par les descendants de la classe.


Variable de classe ( @@)

Prenons une classe Foo avec une variable de classe @@iet des accesseurs pour la lecture et l'écriture @@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

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

end

Et une classe dérivée:

class Bar < Foo
end

On voit que Foo et Bar ont la même valeur pour @@i:

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

Et changer @@idans un le change dans les deux:

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

Variable d'instance de classe ( @)

Créons une classe simple avec une variable d'instance de classe @iet des accesseurs pour la lecture et l'écriture @i:

class Foo

  @i = 1

  def self.i
    @i
  end

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

end

Et une classe dérivée:

class Bar < Foo
end

On voit que bien que Bar hérite des accesseurs pour @i, il n'hérite pas de @ilui-même:

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

Nous pouvons définir Bar's @isans affecter les Foo @i:

Bar.i = 2
p Foo.i    # => 1
p Bar.i    # => 2
Wayne Conrad
la source
1
Pourquoi renvoyer des variables d'instance à l'aide de méthodes de classe? Rencontrez-vous souvent cette situation?
sekmo
1
@sekmo Les accesseurs de ces exemples renvoient soit des variables d'instance de classe , qui appartiennent à la classe, soit des variables de classe , qui appartiennent à la hiérarchie de classes. Ils ne renvoient pas de variables d'instance simples qui appartiennent à des instances de la classe. Les termes «variable d'instance», «variable d'instance de classe» et «variable de classe» sont assez déroutants, n'est-ce pas?
Wayne Conrad
72

Vous devez d'abord comprendre que les classes sont aussi des instances - des instances de la Classclasse.

Une fois que vous comprenez cela, vous pouvez comprendre qu'une classe peut être associée à des variables d'instance comme un objet normal (lu: non-classe).

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!"

Notez qu'une variable d'instance sur Helloest complètement indépendante et distincte d'une variable d'instance sur une instance deHello

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!"

Une variable de classe, en revanche, est une sorte de combinaison des deux ci-dessus, car elle est accessible sur Helloelle-même et ses instances, ainsi que sur les sous-classes de Helloet leurs instances:

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!"

Beaucoup de gens disent qu'il faut éviter en class variablesraison du comportement étrange ci-dessus et recommandent d'utiliser à la class instance variablesplace.

horseyguy
la source
2
+1 Super Explication! C'est quelque chose que tout programmeur novice sur Ruby devrait digérer.
TomZ
0

Je souhaite également ajouter que vous pouvez accéder à la variable de classe ( @@) à partir de n'importe quelle instance de la classe

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

Mais vous ne pouvez pas faire de même pour la variable d'instance de classe (@ )

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
la source
-1: Ceci est incorrect: les variables d'instance de classe sont accessibles à partir de chaque instance de cette classe (à condition que les accesseurs soient présents). En outre, votre exemple montre des variables d'instance régulières, pas des variables d'instance de classe. Les variables d'instance de classe sont déclarées en dehors des méthodes et sont fondamentalement différentes des variables d'instance régulières et des variables de classe.
The Pellmeister
Comment pouvez-vous accéder aux variables d'instance de classe à partir de chaque instance de cette classe?
Evan Ross
Avez-vous lu la réponse de Wayne Conrad? Cela l'explique littéralement. stackoverflow.com/a/3803089/439592
The Pellmeister
Je sais que vous pouvez accéder à des méthodes de classe comme Foo.i, mais comment pouvez-vous faire la même chose pour chaque instance de cette classe Foo.new.i?
Evan Ross
En utilisant #class. #classPersonnellement, je pense que les usages quand on les appelle, mais ce selfsont des odeurs de code. Vous pouvez même aller plus loin et implémenter des accesseurs d'instance iet i=ce délégué à leurs #classéquivalents, auquel cas vous pouvez le faire Foo.new.i. Je ne recommande pas de le faire car cela crée une interface déroutante, suggérant que vous modifiez un membre d'objet.
The Pellmeister