Was bedeutet @@ Variable in Ruby?

162

Was sind Ruby-Variablen, denen bei Zeichen ( @@) ein Double vorangestellt ist ? Mein Verständnis einer Variablen, der ein at-Zeichen vorangestellt ist, ist, dass es sich um eine Instanzvariable handelt, wie in PHP:

PHP-Version

class Person {

    public $name;

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

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

Rubinäquivalent

class Person

    def set_name(name)
        @name = name
    end

    def get_name()
        @name
    end
end

Was bedeutet das doppelte at-Zeichen @@und wie unterscheidet es sich von einem einzelnen at-Zeichen?

Andrew
quelle
103
Ich weiß es nicht, aber ich habe das Gefühl, es starrt mich an. Ich habe ein wenig Angst, jetzt in Ruby zu
programmieren
2
TL; DR für die Öffentlichkeit: 99 von 100 Mal würde ich "Klasseninstanz" -Variablen ( @innerhalb von selfMethoden) verwenden, keine Klassenvariablen ( @@). Siehe die Litanei der Gründe dafür in den Antworten unten.
WattsInABox

Antworten:

240

Eine Variable mit dem Präfix @ist eine Instanzvariable , während eine mit dem Präfix @@eine Klassenvariable ist . Schauen Sie sich das folgende Beispiel an. Die Ausgabe erfolgt in den Kommentaren am Ende der putsZeilen:

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

Sie können sehen, dass dies @@sharedzwischen den Klassen geteilt wird. Durch Festlegen des Werts in einer Instanz von eins wird der Wert für alle anderen Instanzen dieser Klasse und sogar für untergeordnete Klassen, bei denen eine Variable benannt ist @shared, mit eins geändert@ nicht wäre.

[Aktualisieren]

Wie Phrogz in den Kommentaren erwähnt, ist es in Ruby üblich, Daten auf Klassenebene mit einer Instanzvariablen für die Klasse selbst zu verfolgen . Dies kann ein schwieriges Thema sein, um sich Gedanken zu machen, und es gibt viele zusätzliche Informationen zu diesem Thema, aber denken Sie daran, dass es die ClassKlasse ändert , aber nur die Instanz der ClassKlasse, mit der Sie arbeiten. Ein Beispiel:

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

Ich habe das SquareBeispiel (welche Ausgaben nil) beigefügt , um zu demonstrieren, dass sich dies möglicherweise nicht zu 100% wie erwartet verhält. das Artikel, den ich oben verlinkt habe, enthält viele zusätzliche Informationen zu diesem Thema.

Denken Sie auch daran, dass Sie, wie bei den meisten Daten, mit Klassenvariablen in einer Multithread-Umgebung äußerst vorsichtig sein sollten , wie im Kommentar von dmarkow angegeben.

Michelle Tilley
quelle
1
Diese Antwort wäre meiner Meinung nach perfekt, wenn Sie Code einfügen würden, der zeigt, wie Sie eine Instanzvariable auf Klassenebene verwenden können, um Daten auf Klassenebene zu verfolgen, ohne das „seltsame“ Verhalten, Daten zwischen Unterklassen auszutauschen.
Phrogz
3
Ich möchte auch darauf hinweisen, dass Klassenvariablen in einer Umgebung mit mehreren Threads (z. B. Rails) gefährlich / unzuverlässig sein können
Dylan Markow
Hmm ... in gewisser Weise klingt es wie statische Variablen in PHP, aber der Vererbungsteil ist anders. Ich glaube nicht, dass PHP genau so etwas hat.
Andrew
5
Ich verstehe nicht, was der ruby class << self endBlock tut, insbesondere den Operator <<.
Davidtingsu
1
für andere, die verwirrt sind, class << selfsehen Sie dies
Kapad
36

@- Instanzvariable einer Klasse
@@- Klassenvariable, in einigen Fällen auch als statische Variable bezeichnet

Eine Klassenvariable ist eine Variable, die von allen Instanzen einer Klasse gemeinsam genutzt wird. Dies bedeutet, dass für alle aus dieser Klasse instanziierten Objekte nur ein Variablenwert vorhanden ist. Wenn eine Objektinstanz den Wert der Variablen ändert, ändert sich dieser neue Wert im Wesentlichen für alle anderen Objektinstanzen.

Eine andere Art, an Klassenvariablen zu denken, ist als globale Variablen im Kontext einer einzelnen Klasse. Klassenvariablen werden deklariert, indem dem Variablennamen zwei @Zeichen ( @@) vorangestellt werden . Klassenvariablen müssen zum Zeitpunkt der Erstellung initialisiert werden

Shaunak
quelle
10

@@ bezeichnet eine Klassenvariable, dh sie kann vererbt werden.

Dies bedeutet, dass beim Erstellen einer Unterklasse dieser Klasse die Variable geerbt wird. Wenn Sie also eine Klasse Vehiclemit der Klassenvariablen haben, @@number_of_wheelswenn Sie eine erstellen, class Car < Vehiclehat auch diese die Klassenvariable@@number_of_wheels

Fareesh Vijayarangam
quelle
Dies bedeutet, dass beim Erstellen einer Unterklasse dieser Klasse die Variable geerbt wird. Wenn Sie also eine Klasse Vehiclemit der Klassenvariablen haben @@number_of_wheels, class Car < Vehicledann hat sie auch die Klassenvariable@@number_of_wheels
Fareesh Vijayarangam
12
Wenn ich ein class Vehiclewith habe @number_of_wheels, class Car < Vehiclewird auch eine Instanzvariable aufgerufen @number_of_wheels. Der Hauptunterschied zu Klassenvariablen besteht darin, dass die Klassen dieselbe Variable haben, z. B. wenn Sie eine ändern, werden die anderen geändert.
Michelle Tilley
1

@ und @@ in Modulen funktionieren auch anders, wenn eine Klasse dieses Modul erweitert oder enthält.

So gegeben

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 

Dann erhalten Sie die unten aufgeführten Ausgaben als Kommentare

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

Verwenden Sie also @@ in Modulen für Variablen, die für alle Verwendungszwecke verwendet werden sollen, und @ in Modulen für Variablen, die für jeden Verwendungskontext separat verwendet werden sollen.

Tantallion
quelle
1

Die Antworten sind teilweise korrekt, da @@ tatsächlich eine Klassenvariable ist, die pro Klassenhierarchie angegeben ist, was bedeutet, dass sie von einer Klasse, ihren Instanzen und ihren untergeordneten Klassen und ihren Instanzen gemeinsam genutzt wird.

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

Dies wird ausgegeben

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

Es gibt also nur eine @@ -Variable für Personen-, Studenten- und Abschlussklassen, und alle Klassen- und Instanzmethoden dieser Klassen beziehen sich auf dieselbe Variable.

Es gibt eine andere Möglichkeit, eine Klassenvariable zu definieren, die für ein Klassenobjekt definiert ist (Denken Sie daran, dass jede Klasse tatsächlich eine Instanz von etwas ist, das tatsächlich die Klassenklasse ist, aber es ist eine andere Geschichte). Sie verwenden die @ -Notation anstelle von @@, können jedoch nicht über Instanzmethoden auf diese Variablen zugreifen. Sie benötigen Klassenmethoden-Wrapper.

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(",")

Hier ist @people pro Klasse anstelle der Klassenhierarchie einzeln, da es sich tatsächlich um eine Variable handelt, die in jeder Klasseninstanz gespeichert ist. Dies ist die Ausgabe:

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

Ein wichtiger Unterschied besteht darin, dass Sie nicht direkt über Instanzmethoden auf diese Klassenvariablen (oder Klasseninstanzvariablen, die Sie sagen können) zugreifen können, da @people in einer Instanzmethode auf eine Instanzvariable dieser bestimmten Instanz der Klassen Person, Student oder Graduate verweisen würde .

Während andere Antworten korrekt angeben, dass @myvariable (mit einfacher @ -Notation) immer eine Instanzvariable ist, bedeutet dies nicht unbedingt, dass es sich nicht um eine einzelne gemeinsam genutzte Variable für alle Instanzen dieser Klasse handelt.

Cagatay Kalan
quelle