Переменные экземпляра уровня класса в Ruby

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

@@ — это переменная класса, эквивалентная переменной экземпляра (@) в экземпляре класса.

Но что такое переменная экземпляра (@) при использовании на уровне класса? Если он помещает определение в экземпляр класса, то почему бы не определить его в инициализаторе?

class MyClass
  cattr_reader :class_variable

  def self.new_instance(cv, cliv, iv)
    @@class_variable = cv
    @class_level_instance_variable = cliv
    self.new(iv)
  end

  def initialize(iv)
    @instance_variable = iv
  end

  def use
    puts "class_var=#{self.class.class_variable.inspect}\ninst_var=#{@instance_variable.inspect}\ncliv=#{@class_level_instance_variable.inspect}"
  end
end

c = []
c << MyClass.new_instance(1,2,3)
c[0].use
c << MyClass.new_instance(4,5,6)
c[1].use
c << MyClass.new_instance(7,8,9)
c[2].use

c[0].use
c[1].use
c[2].use

person Paul    schedule 29.08.2014    source источник
comment
Когда вы устанавливаете переменную экземпляра на уровне класса, она становится переменной экземпляра класса, которая немного отличается (и не совсем эквивалентна) переменной класса. Подробнее здесь: railstips .org/blog/archives/2006/11/18/   -  person Marek Lipka    schedule 29.08.2014
comment
@Marek Lipka: Итак, если я правильно понял, переменные экземпляра уровня класса полностью эквивалентны предварительно инициализированным переменным экземпляра уровня экземпляра, и если я создаю несколько экземпляров класса в методе класса, каждый из этих экземпляров инициализируется этой переменной.   -  person Paul    schedule 29.08.2014
comment
Нет, такого поведения нет вообще. Я только имею в виду, что переменная экземпляра уровня класса является обычной переменной экземпляра, но привязана к объекту класса (помните, что в Ruby классы также являются объектами).   -  person Marek Lipka    schedule 29.08.2014
comment
@Paul: Почему бы тебе не добавить код, чтобы проиллюстрировать, что ты имеешь в виду?   -  person Sergio Tulentsev    schedule 29.08.2014


Ответы (2)


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

Вот модифицированная версия вашего кода

require 'active_support/core_ext'

class MyClass
  cattr_reader :class_variable

  def self.new_instance(cv, cliv, iv)
    @@class_variable = cv
    @class_level_instance_variable = cliv
    self.new(iv)
  end

  def initialize(iv)
    @instance_variable = iv
  end

  def use
    puts "class_var=#{self.class.class_variable.inspect}"
    puts "class inst var: #{self.class.instance_variable_get(:@class_level_instance_variable)}"
    puts "inst_var=#{@instance_variable.inspect}"
  end
end

c = []
c << MyClass.new_instance(1,2,3)
c << MyClass.new_instance(4,5,6)
c << MyClass.new_instance(7,8,9)

c[0].use
c[1].use
c[2].use
# >> class_var=7
# >> class inst var: 8
# >> inst_var=3
# >> class_var=7
# >> class inst var: 8
# >> inst_var=6
# >> class_var=7
# >> class inst var: 8
# >> inst_var=9

Видите ли, class inst var всегда равен 8 (точно так же, как class var всегда равен 7). Это связано с тем, что вы выводите значения после внесения всех изменений. А поскольку переменные уровня класса являются общими, последняя модификация побеждает.

c << MyClass.new_instance(7,8,9)

Если бы вы выводили из инициализатора (как это было в вашей первой версии кода), вы бы увидели другие результаты.

# >> class_var=1
# >> class inst var: 2
# >> inst_var=3
# >> class_var=4
# >> class inst var: 5
# >> inst_var=6
# >> class_var=7
# >> class inst var: 8
# >> inst_var=9
person Sergio Tulentsev    schedule 29.08.2014
comment
Таким образом, переменная экземпляра уровня класса дает возможность общаться между дочерними элементами, поскольку передается по ссылке на каждый экземпляр. Верно? - person Paul; 29.08.2014
comment
Да, он является общим для всех экземпляров класса. - person Sergio Tulentsev; 29.08.2014

Надеюсь, этот пример объяснит разницу между переменными @ (экземпляр) и @@ (класс).

class Animal
  @@total_count = 0

  def self.total_count
    @@total_count
  end

  def initialize
    @@total_count += 1
  end
end

class Cat < Animal
end

Animal.new
Animal.new
Cat.new

Animal.total_count # => 3
Cat.total_count # => 3

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

class Animal
  class << self
    attr_accessor :total_count
  end

  @total_count = 0

  def self.total_count
    @total_count
  end

  def initialize
    self.class.total_count += 1
  end
end

class Cat < Animal
  @total_count = 0
end

Animal.new
Animal.new
Cat.new

Animal.total_count # => 2
Cat.total_count # => 1
person jan.zikan    schedule 29.08.2014