Что означает переменная @@ в Ruby? - PullRequest
148 голосов
/ 05 мая 2011

Каковы переменные Ruby, начинающиеся с двойных в знаках (@@)?Мое понимание переменной, которой предшествует знак at, заключается в том, что это переменная экземпляра, например, в PHP:

версия PHP

class Person {

    public $name;

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

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

эквивалент Ruby

class Person

    def set_name(name)
        @name = name
    end

    def get_name()
        @name
    end
end

Что означает двойное в знаке @@ и чем оно отличается от одинарного в знаке?

Ответы [ 5 ]

227 голосов
/ 05 мая 2011

Переменная с префиксом @ является переменной экземпляра , а переменная с @@ является переменной класса .Посмотрите на следующий пример;его вывод находится в комментариях в конце строк puts:

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

Вы можете видеть, что @@shared является общим для классов;установка значения в один экземпляр изменяет значение для всех других экземпляров этого класса и даже дочерних классов, где переменная с именем @shared, с одним @, не будет.

[Обновление]

Как упоминает Фрогз в комментариях, в Ruby распространена идиома - отслеживать данные уровня класса с помощью переменной экземпляра в самом классе .Это может быть сложная тема, чтобы обернуть ваш разум, и есть множество дополнительных чтений на эту тему, но подумайте об этом как об изменении класса Class, но только экземпляр класса Class, с которым вы работаете.Пример:

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

Я включил пример Square (который выводит nil), чтобы продемонстрировать, что это может вести себя не на 100%, как вы ожидаете;статья , на которую я ссылался выше , содержит много дополнительной информации по этому вопросу.

Также имейте в виду, что, как и в случае с большинством данных, вы должны быть чрезвычайно осторожны с переменными класса в многопоточная среда , согласно комментарию dmarkow.

35 голосов
/ 05 мая 2011

@ - переменная экземпляра класса
@@ - переменная класса, в некоторых случаях также называемая статической переменной

Переменная класса - это переменная, которая используется всеми экземплярами класса. Это означает, что существует только одно значение переменной для всех объектов, созданных из этого класса. Если один экземпляр объекта изменит значение переменной, это новое значение существенно изменится для всех остальных экземпляров объекта.

Еще один способ мышления о переменных класса - это глобальные переменные в контексте одного класса. Переменные класса объявляются с префиксом имени переменной двумя @ символами (@@). Переменные класса должны быть инициализированы во время создания

10 голосов
/ 05 мая 2011

@@ обозначает переменную класса, т.е. она может быть унаследована.

Это означает, что если вы создадите подкласс этого класса, он унаследует переменную.Так что, если у вас есть класс Vehicle с переменной класса @@number_of_wheels, то, если вы создадите class Car < Vehicle, тогда он также будет иметь переменную класса @@number_of_wheels

1 голос
/ 03 августа 2018

Ответы частично верны, потому что @@ на самом деле является переменной класса, которая относится к иерархии классов, что означает, что она является общей для класса, его экземпляров и классов-потомков и их экземпляров.

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

Это выдаст

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

Таким образом, существует только одна и та же переменная @@ для классов Person, Student и Graduate, и все методы классов и экземпляров этих классов ссылаются на одну и ту же переменную.

Существует еще один способ определения переменной класса, которая определена для объекта класса (помните, что каждый класс на самом деле является экземпляром чего-то, что на самом деле является классом Class, но это другая история). Вы используете @ нотацию вместо @@, но вы не можете получить доступ к этим переменным из методов экземпляра. Вы должны иметь обертки метода класса.

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

Здесь @people является единичным для класса вместо иерархии классов, поскольку на самом деле это переменная, хранящаяся в каждом экземпляре класса. Это вывод:

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

Одно важное отличие состоит в том, что вы не можете получить доступ к этим переменным класса (или к переменным экземпляра класса, которые вы можете сказать) напрямую из методов экземпляра, потому что @people в методе экземпляра будет ссылаться на переменную экземпляра этого конкретного экземпляра Person или Student. или выпускные классы.

Таким образом, хотя в других ответах правильно указывается, что @myvariable (с одиночной @ нотацией) всегда является переменной экземпляра, это не обязательно означает, что она не является единой общей переменной для всех экземпляров этого класса.

1 голос
/ 16 мая 2017

@ и @@ в модулях также работают по-разному, когда класс расширяет или включает этот модуль.

Так дано

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 

Тогда вы получите результаты, показанные ниже в виде комментариев

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

Поэтому используйте @@ в модулях для переменных, которые вы хотите использовать для всех их применений, и используйте @ в модулях для переменных, которые вы хотите использовать отдельно для каждого контекста использования.

...