¿Diferencia entre variables de clase y variables de instancia de clase?

Respuestas:

151

Una variable de clase ( @@) se comparte entre la clase y todos sus descendientes. @Los descendientes de la clase no comparten una variable de instancia de clase ( ).


Variable de clase ( @@)

Tengamos una clase Foo con una variable de clase @@iy accesos para leer y escribir @@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

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

end

Y una clase derivada:

class Bar < Foo
end

Vemos que Foo y Bar tienen el mismo valor para @@i:

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

Y cambiar @@ien uno lo cambia en ambos:

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

Variable de instancia de clase ( @)

Hagamos una clase simple con una variable de instancia de clase @iy accesos para leer y escribir @i:

class Foo

  @i = 1

  def self.i
    @i
  end

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

end

Y una clase derivada:

class Bar < Foo
end

Vemos que aunque Bar hereda los descriptores de acceso @i, no se hereda a @isí mismo:

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

Podemos configurar Bar's @isin afectar a Foo's @i:

Bar.i = 2
p Foo.i    # => 1
p Bar.i    # => 2
Wayne Conrad
fuente
1
¿Por qué devolver variables de instancia utilizando métodos de clase? ¿Te encuentras a menudo con esta situación?
sekmo
1
@sekmo Los descriptores de acceso en estos ejemplos devuelven variables de instancia de clase , que pertenecen a la clase, o variables de clase , que pertenecen a la jerarquía de clases. No devuelven variables de instancia simples que pertenecen a instancias de la clase. Los términos "variable de instancia", "instancia de clase variable" y "variable de clase" son bastante confusos, ¿no es así?
Wayne Conrad
72

Primero debe comprender que las clases también son instancias, instancias de la Classclase.

Una vez que comprenda eso, puede comprender que una clase puede tener variables de instancia asociadas con ella al igual que un objeto normal (léase: no clase).

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

Tenga en cuenta que una variable de instancia en no Helloestá relacionada y es distinta de una variable de instancia en una instancia 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!"

Una variable de clase, por otro lado, es una especie de combinación de las dos anteriores, ya que es accesible en Hellosí misma y sus instancias, así como en subclases de Helloy sus instancias:

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

Muchas personas dicen que se debe evitar class variablesdebido al comportamiento extraño anterior y recomiendan el uso de class instance variables.

caballo
fuente
2
+1 Super Explicación! Esto es algo que todo programador nuevo en Ruby debería digerir.
TomZ
0

También quiero agregar que puede obtener acceso a la variable de clase ( @@) desde cualquier instancia de la clase

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

Pero no puede hacer lo mismo con la variable de instancia de clase ( @)

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
fuente
-1: Esto es incorrecto: se puede acceder a las variables de instancia de clase desde cada instancia de esa clase (siempre que los descriptores de acceso estén presentes). Además, su ejemplo muestra variables de instancia regulares, no variables de instancia de clase. Las variables de instancia de clase se declaran fuera de los métodos y son fundamentalmente diferentes tanto de las variables de instancia normales como de las variables de clase.
The Pellmeister
¿Cómo se puede acceder a las variables de instancia de clase desde cada instancia de esa clase?
Evan Ross
¿Ha leído la respuesta de Wayne Conrad? Eso lo explica literalmente. stackoverflow.com/a/3803089/439592
The Pellmeister
Sé que puede obtener acceso desde métodos de clase como Foo.i, pero ¿cómo puede hacer lo mismo para cada instancia de esta clase como Foo.new.i?
Evan Ross
Utilizando #class. Sin embargo, personalmente creo que los #classusos se usan cuando se llama a cualquier cosa que no selfsean olores de código. Incluso puede ir un paso más allá e implementar accesos de instancia iy i=delegar a sus #classequivalentes, en cuyo caso puede hacerlo Foo.new.i. No recomiendo hacer eso ya que crea una interfaz confusa, lo que sugiere que está modificando un miembro del objeto.
The Pellmeister