Я забыл, что в Ruby существует концепция «переменной экземпляра класса». В любом случае проблема ОП казалась загадочной и до сих пор не рассматривалась ни в одном из ответов, за исключением подсказки в ответе kch: это проблема масштаба. (Добавлено при редактировании: на самом деле ответ sris действительно затрагивает этот момент в конце, но я все равно оставлю этот ответ, так как я думаю, что пример кода может быть полезен для понимания проблемы.)
В классе Ruby имя переменной, начинающееся с @
, может относиться к одной из двух переменных: либо к переменной экземпляра, либо к переменной экземпляра класса. >, в зависимости от того, где в классе он упоминается. Это довольно тонкая ошибка.
Пример пояснит суть. Вот небольшой тестовый класс Ruby (весь код протестирован в irb):
class T
@@class_variable = "BBQ"
@class_instance_variable_1 = "WTF"
@class_instance_variable_2 = "LOL"
def self.class_method
puts "@@class_variable == #{@@class_variable || 'nil'}"
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
def initialize
@instance_variable = "omg"
# The following line does not assign a value to the class instance variable,
# but actually declares an instance variable withthe same name!
@class_instance_variable_1 = "wtf"
puts "@@class_variable == #{@@class_variable || 'nil'}"
# The following two lines do not refer to the class instance variables,
# but to the instance variables with the same names.
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
def instance_method
puts "@@class_variable == #{@@class_variable || 'nil'}"
# The following two lines do not refer to the class instance variables,
# but to the instance variables with the same names.
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
end
Я назвал переменные в соответствии с тем, что я думал, хотя это не всегда так:
irb> T.class_method
@@class_variable == BBQ
@class_instance_variable_1 == WTF # the value of the class instance variable
@class_instance_variable_2 == LOL # the value of the class instance variable
@instance_variable == nil # does not exist in the class scope
=> nil
irb> t = T.new
@@class_variable == BBQ
@class_instance_variable_1 == wtf # the value of the instance variable
@class_instance_variable_2 == nil # the value of the instance variable
@instance_variable == omg
=> #<T:0x000000015059f0 @instance_variable="omg", @class_instance_variable_1="wtf">
irb> t.instance_method
@@class_variable == BBQ
@class_instance_variable_1 == wtf # the value of the instance variable
@class_instance_variable_2 == nil # the value of the instance variable
@instance_variable == omg
=> nil
irb> T.class_method
@@class_variable == BBQ
@class_instance_variable_1 == WTF # the value of the class instance variable
@class_instance_variable_2 == LOL # the value of the class instance variable
@instance_variable == nil # does not exist in the class scope
=> nil
@@class_variable
и @instance_variable
всегда ведут себя так, как вы ожидаете: первый определен на уровне класса, и независимо от того, упоминается ли он в методе класса или в методе экземпляра, он содержит назначенное ему значение вверху. Последний получает значение только в объекте класса T
, поэтому в методе класса он ссылается на неизвестную переменную, значение которой равно nil
.
Метод класса с образным именем class_method
выводит значения @@class_variable
и два @class_instance_variable
, как и ожидалось, то есть как инициализировано в верхней части класса. Однако в методах экземпляра initialize
и instance_method
осуществляется доступ к разным переменным, с тем же именем, то есть к переменным экземпляра, а не к переменным экземпляра класса. .
Вы можете видеть, что присваивание в методе initialize
не повлияло на переменную экземпляра класса @class_instance_variable_1
, потому что более поздний вызов class_method
выводит ее старое значение, "WTF"
. Вместо этого метод initialize
объявил новую переменную экземпляра, которая также названа (ошибочно) @class_instance_variable_1
. Присвоенное ему значение "wtf"
выводится методами initialize
и instance_method
.
Переменная @class_instance_variable_2
в примере кода эквивалентна переменной @hello
в исходной задаче: она объявлена и инициализирована как переменная экземпляра класса, но когда метод экземпляра ссылается на переменную с таким именем, он фактически видит переменную экземпляра с то же имя -- имя, которое никогда не объявлялось, поэтому его значение равно нулю.
person
Teemu Leisti
schedule
25.12.2014