Cosa significa variabile @@ in Ruby?


162

Quali sono le variabili Ruby precedute da double at signs ( @@)? La mia comprensione di una variabile preceduta da un segno at è che si tratta di una variabile di istanza, come questa in PHP:

Versione PHP

class Person {

    public $name;

    public function setName($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

Equivalente rubino

class Person

    def set_name(name)
        @name = name
    end

    def get_name()
        @name
    end
end

Cosa significa il doppio al segno @@e in che cosa differisce da un singolo al segno?


103
Non lo so, ma ho la sensazione che mi stia fissando. Ho un po 'paura di scrivere il codice in Ruby ora ...
corsiKa

2
TL; DR per il pubblico: 99 volte su 100, utilizzerei le variabili di "istanza di classe" ( metodi @interni self) e non le variabili di classe ( @@). Vedi la litania dei motivi per cui nelle risposte di seguito.
WattsInABox,

Risposte:


240

Una variabile con il prefisso @è una variabile di istanza , mentre una con il prefisso @@è una variabile di classe . Guarda il seguente esempio; il suo output è nei commenti alla fine delle putsrighe:

class Test
  @@shared = 1

  def value
    @@shared
  end

  def value=(value)
    @@shared = value
  end
end

class AnotherTest < Test; end

t = Test.new
puts "t.value is #{t.value}" # 1
t.value = 2
puts "t.value is #{t.value}" # 2

x = Test.new
puts "x.value is #{x.value}" # 2

a = AnotherTest.new
puts "a.value is #{a.value}" # 2
a.value = 3
puts "a.value is #{a.value}" # 3
puts "t.value is #{t.value}" # 3
puts "x.value is #{x.value}" # 3

Puoi vedere che @@sharedè condiviso tra le classi; l'impostazione del valore in un'istanza di uno modifica il valore per tutte le altre istanze di quella classe e anche le classi figlio, dove una variabile denominata @shared, con una @, non sarebbe.

[Aggiornare]

Come Phrogz menziona nei commenti, è un linguaggio comune in Ruby tenere traccia dei dati a livello di classe con una variabile di istanza sulla classe stessa . Questo può essere un argomento complicato per avvolgere la tua mente, e ci sono molte altre letture sull'argomento, ma pensaci come modificare la Classclasse, ma solo l'istanza della Classclasse con cui stai lavorando. Un esempio:

class Polygon
  class << self
    attr_accessor :sides
  end
end

class Triangle < Polygon
  @sides = 3
end

class Rectangle < Polygon
  @sides = 4
end

class Square < Rectangle
end

class Hexagon < Polygon
  @sides = 6
end

puts "Triangle.sides:  #{Triangle.sides.inspect}"  # 3
puts "Rectangle.sides: #{Rectangle.sides.inspect}" # 4
puts "Square.sides:    #{Square.sides.inspect}"    # nil
puts "Hexagon.sides:   #{Hexagon.sides.inspect}"   # 6

Ho incluso l' Squareesempio (quali output nil) per dimostrare che questo potrebbe non comportarsi al 100% come previsto; l' articolo che ho linkato sopra contiene molte informazioni aggiuntive sull'argomento.

Inoltre, tieni presente che, come con la maggior parte dei dati, devi essere estremamente attento con le variabili di classe in un ambiente multithread , come da commento di dmarkow.


1
Questa risposta sarebbe perfetta IMHO se includessi il codice che mostra come è possibile utilizzare una variabile di istanza a livello di classe per tenere traccia dei dati a livello di classe senza il comportamento "strano" della condivisione di dati tra sottoclassi.
Phrogz,

3
Vorrei anche sottolineare che le variabili di classe possono essere pericolose / inaffidabili in un ambiente multi-thread (ad esempio Rails)
Dylan Markow

Hmm ... in un certo senso sembra variabili statiche in PHP, ma la parte ereditaria è diversa. Non credo che PHP abbia qualcosa di esattamente simile a questo.
Andrew,

5
Non capisco cosa fa il ruby class << self endblocco, in particolare l'operatore <<.
davidtingsu,

1
per gli altri che sono confusi class << selfvedere questo
Kapad

37

@- Variabile di istanza di una classe
@@- Variabile di classe, chiamata anche come variabile statica in alcuni casi

Una variabile di classe è una variabile condivisa tra tutte le istanze di una classe. Ciò significa che esiste un solo valore variabile per tutti gli oggetti istanziati da questa classe. Se un'istanza di un oggetto cambia il valore della variabile, quel nuovo valore cambierà essenzialmente per tutte le altre istanze di oggetto.

Un altro modo di pensare alle variabili di classe è come variabili globali nel contesto di una singola classe. Le variabili di classe vengono dichiarate prefissando il nome della variabile con due @caratteri ( @@). Le variabili di classe devono essere inizializzate al momento della creazione


10

@@ indica una variabile di classe, ovvero può essere ereditata.

Ciò significa che se si crea una sottoclasse di quella classe, erediterà la variabile. Quindi, se si dispone di una classe Vehiclecon la variabile di classe, @@number_of_wheelsquindi se si crea una class Car < Vehicle, anche questa avrà la variabile di classe@@number_of_wheels


Ciò significa che se si crea una sottoclasse di quella classe, erediterà la variabile. Quindi se hai una classe Vehiclecon la variabile di classe, @@number_of_wheelsquindi se ne crei una class Car < Vehicleallora avrà anche la variabile di classe@@number_of_wheels
Fareesh Vijayarangam,

12
Se ho un class Vehiclecon @number_of_wheels, allora class Car < Vehicleavrò anche una variabile di istanza chiamata @number_of_wheels. La differenza chiave con le variabili di classe è che le classi hanno la stessa variabile, ad esempio cambiando una cambia l'altra.
Michelle Tilley,

1

@ e @@ nei moduli funzionano anche in modo diverso quando una classe estende o include quel modulo.

Così dato

module A
    @a = 'module'
    @@a = 'module'

    def get1
        @a          
    end     

    def get2
        @@a         
    end     

    def set1(a) 
        @a = a      
    end     

    def set2(a) 
        @@a = a     
    end     

    def self.set1(a)
        @a = a      
    end     

    def self.set2(a)
        @@a = a     
    end     
end 

Quindi ottieni gli output di seguito mostrati come commenti

class X
    extend A

    puts get1.inspect # nil
    puts get2.inspect # "module"

    @a = 'class' 
    @@a = 'class' 

    puts get1.inspect # "class"
    puts get2.inspect # "module"

    set1('set')
    set2('set')

    puts get1.inspect # "set" 
    puts get2.inspect # "set" 

    A.set1('sset')
    A.set2('sset')

    puts get1.inspect # "set" 
    puts get2.inspect # "sset"
end 

class Y
    include A

    def doit
        puts get1.inspect # nil
        puts get2.inspect # "module"

        @a = 'class'
        @@a = 'class'

        puts get1.inspect # "class"
        puts get2.inspect # "class"

        set1('set')
        set2('set')

        puts get1.inspect # "set"
        puts get2.inspect # "set"

        A.set1('sset')
        A.set2('sset')

        puts get1.inspect # "set"
        puts get2.inspect # "sset"
    end
end

Y.new.doit

Quindi usa @@ nei moduli per le variabili che desideri comuni a tutti i loro usi e usa @ nei moduli per le variabili che desideri separate per ogni contesto d'uso.


1

Le risposte sono parzialmente corrette perché @@ è in realtà una variabile di classe che è per gerarchia di classi, il che significa che è condivisa da una classe, dalle sue istanze e dalle sue classi discendenti e dalle loro istanze.

class Person
  @@people = []

  def initialize
    @@people << self
  end

  def self.people
    @@people
  end
end

class Student < Person
end

class Graduate < Student
end

Person.new
Student.new

puts Graduate.people

Questo produrrà

#<Person:0x007fa70fa24870>
#<Student:0x007fa70fa24848>

Quindi esiste solo una stessa variabile @@ per le classi Persona, Studente e Laureato e tutti i metodi di classe e istanza di queste classi si riferiscono alla stessa variabile.

C'è un altro modo di definire una variabile di classe che è definita su un oggetto di classe (Ricorda che ogni classe è in realtà un'istanza di qualcosa che è in realtà la classe di classe ma è un'altra storia). Si utilizza @ notation anziché @@ ma non è possibile accedere a queste variabili dai metodi di istanza. È necessario disporre di wrapper con metodo di classe.

class Person

  def initialize
    self.class.add_person self
  end

  def self.people
    @people
  end

  def self.add_person instance
    @people ||= []
    @people << instance
  end
end

class Student < Person
end

class Graduate < Student
end

Person.new
Person.new
Student.new
Student.new
Graduate.new
Graduate.new

puts Student.people.join(",")
puts Person.people.join(",")
puts Graduate.people.join(",")

Qui, @people è singolo per classe anziché gerarchia di classi perché in realtà è una variabile memorizzata su ogni istanza di classe. Questo è l'output:

#<Student:0x007f8e9d2267e8>,#<Student:0x007f8e9d21ff38>
#<Person:0x007f8e9d226158>,#<Person:0x007f8e9d226608>
#<Graduate:0x007f8e9d21fec0>,#<Graduate:0x007f8e9d21fdf8> 

Una differenza importante è che non puoi accedere a queste variabili di classe (o variabili di istanza di classe che puoi dire) direttamente dai metodi di istanza perché @people in un metodo di istanza farebbe riferimento a una variabile di istanza di quella specifica istanza delle classi Persona o Studente o Laurea .

Quindi, mentre altre risposte affermano correttamente che @myvariable (con singola @ notazione) è sempre una variabile di istanza, ciò non significa necessariamente che non sia una singola variabile condivisa per tutte le istanze di quella classe.

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.