Differenza tra variabili di classe e variabili di istanza di classe?


Risposte:


151

Una variabile di classe ( @@) è condivisa tra la classe e tutti i suoi discendenti. Una variabile di istanza di classe ( @) non è condivisa dai discendenti della classe.


Variabile di classe ( @@)

Facciamo una classe Foo con una variabile di classe @@ie funzioni di accesso per la lettura e la scrittura @@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

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

end

E una classe derivata:

class Bar < Foo
end

Vediamo che Foo e Bar hanno lo stesso valore per @@i:

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

E cambiando @@iin uno lo cambia in entrambi:

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

Variabile di istanza di classe ( @)

Creiamo una classe semplice con una variabile di istanza di classe @ie funzioni di accesso per la lettura e la scrittura @i:

class Foo

  @i = 1

  def self.i
    @i
  end

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

end

E una classe derivata:

class Bar < Foo
end

Vediamo che sebbene Bar erediti le @ifunzioni di accesso per , non eredita @ise stesso:

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

Possiamo impostare Bar's @isenza influire su Foo's @i:

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

1
Perché restituire variabili di istanza utilizzando metodi di classe? Ti imbatti spesso in questa situazione?
sekmo

1
@sekmo Le funzioni di accesso in questi esempi restituiscono variabili di istanza di classe , che appartengono alla classe, o variabili di classe , che appartengono alla gerarchia di classi. Non restituiscono semplici variabili di istanza che appartengono a istanze della classe. I termini "variabile di istanza", "variabile di istanza di classe" e "variabile di classe" creano confusione, non è vero?
Wayne Conrad

72

Per prima cosa devi capire che anche le classi sono istanze - istanze della Classclasse.

Una volta capito questo, puoi capire che una classe può avere variabili di istanza associate ad essa proprio come un normale oggetto (leggi: 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!"

Si noti che una variabile di istanza su Helloè completamente non correlata e distinta da una variabile di istanza su un'istanza diHello

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 variabile di classe d' altra parte è una sorta di combinazione delle due precedenti, in quanto accessibile su Hellose stessa e sulle sue istanze, nonché sulle sottoclassi di Helloe sulle loro istanze:

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

Molte persone dicono di evitare a class variablescausa dello strano comportamento di cui sopra e raccomandano invece l'uso di class instance variables.


2
+1 Super Spiegazione! Questo è qualcosa che ogni programmatore nuovo di Ruby dovrebbe digerire.
TomZ

0

Inoltre voglio aggiungere che puoi accedere alla variabile di classe ( @@) da qualsiasi istanza della 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

Ma non puoi fare lo stesso per la variabile di istanza di 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

-1: non è corretto: è possibile accedere alle variabili di istanza di classe da ogni istanza di quella classe (a condizione che siano presenti le funzioni di accesso). Inoltre, il tuo esempio mostra variabili di istanza regolari, non variabili di istanza di classe. Le variabili di istanza di classe sono dichiarate al di fuori dei metodi e sono fondamentalmente diverse sia dalle variabili di istanza regolari che dalle variabili di classe.
The Pellmeister

Come puoi accedere alle variabili di istanza di classe da ogni istanza di quella classe?
Evan Ross

Hai letto la risposta di Wayne Conrad? Questo lo spiega letteralmente. stackoverflow.com/a/3803089/439592
Il Pellmeister

So che puoi ottenere l'accesso da metodi di classe come Foo.i, ma come puoi fare lo stesso per ogni istanza di questa classe come Foo.new.i?
Evan Ross

Usando #class. Personalmente penso che gli #classusi quando vengono chiamati su qualsiasi cosa, ma selfsono odori di codice. Puoi anche fare un ulteriore passo avanti e implementare le funzioni di accesso alle istanze ie i=quel delegato ai loro #classequivalenti, nel qual caso puoi farlo Foo.new.i. Non consiglio di farlo in quanto crea un'interfaccia confusa, suggerendo che stai modificando un membro dell'oggetto.
The Pellmeister
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.