Разница между переменными класса и переменными экземпляра класса?

Ответы:

152

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


Переменная класса ( @@)

У нас есть класс Foo с переменной класса @@iи аксессеры для чтения и записи @@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

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

end

И производный класс:

class Bar < Foo
end

Мы видим, что Foo и Bar имеют одинаковое значение для @@i:

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

И изменение @@iв одном меняет его в обоих:

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

Переменная экземпляра класса ( @)

Давайте создадим простой класс с переменной экземпляра класса @iи средствами доступа для чтения и записи @i:

class Foo

  @i = 1

  def self.i
    @i
  end

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

end

И производный класс:

class Bar < Foo
end

Мы видим, что, хотя Bar наследует методы доступа @i, он не наследует @iсебя:

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

Мы можем установить Bar, @iне затрагивая Foo @i:

Bar.i = 2
p Foo.i    # => 1
p Bar.i    # => 2
Уэйн Конрад
источник
1
Зачем возвращать переменные экземпляра с помощью методов класса? Вы часто сталкиваетесь с такой ситуацией?
sekmo
1
@sekmo Средства доступа в этих примерах возвращают либо переменные экземпляра класса , которые принадлежат классу, либо переменные класса , которые принадлежат иерархии классов. Они не возвращают простые переменные экземпляра, принадлежащие экземплярам класса. Термины «переменная экземпляра», «переменная экземпляра класса» и «переменная класса» довольно сбивают с толку, не так ли?
Уэйн Конрад
72

Сначала вы должны понять, что классы тоже являются экземплярами - экземплярами Classкласса.

Как только вы это поймете, вы сможете понять, что с классом могут быть связаны переменные экземпляра, как с обычным (читай: неклассовым) объектом.

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

Обратите внимание, что переменная экземпляра в экземпляре Helloполностью не связана и отличается от переменной экземпляра в экземпляреHello

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

С другой стороны, переменная класса представляет собой своего рода комбинацию двух вышеперечисленных, поскольку она доступна для Helloсебя и своих экземпляров, а также для подклассов Helloи их экземпляров:

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

Многие люди говорят избегать class variablesиз-за странного поведения, описанного выше, и рекомендуют class instance variablesвместо этого использовать .

конь
источник
2
+1 Супер Объяснение! Это то, что должен усвоить каждый программист, плохо знакомый с Ruby.
TomZ
0

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

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

Но вы не можете сделать то же самое для переменной экземпляра класса ( @)

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: Это неверно: к переменным экземпляра класса можно получить доступ из каждого экземпляра этого класса (при условии, что средства доступа присутствуют). Кроме того, в вашем примере показаны обычные переменные экземпляра, а не переменные экземпляра класса. Переменные экземпляра класса объявляются вне методов и принципиально отличаются как от обычных переменных экземпляра, так и от переменных класса.
The Pellmeister
Как получить доступ к переменным экземпляра класса из каждого экземпляра этого класса?
Эван Росс
Вы читали ответ Уэйна Конрада? Это буквально объясняет это. stackoverflow.com/a/3803089/439592
Пеллмейстер 08
Я знаю, что вы можете получить доступ из таких методов класса, как Foo.i, но как вы можете сделать то же самое для каждого экземпляра этого класса, например Foo.new.i?
Эван Росс
Используя #class. Я лично считаю, что #classиспользование, когда вызывается что-либо, кроме selfзапаха кода. Вы даже можете пойти дальше и реализовать средства доступа к экземплярам iи i=делегировать их #classэквивалентам, и в этом случае вы можете это сделать Foo.new.i. Я не рекомендую делать это, поскольку это создает запутанный интерфейс, предполагающий, что вы изменяете член объекта.
The Pellmeister