Я просто потратил кучу времени, играя в IRB
(ну, PRY
на самом деле), пытаясь выяснить, как переменные класса работают в Ruby, и я полностью озадачен тем, что нашел.Почему переменные класса Ruby ведут себя так странно?
Из того, что я видел (исправьте меня, если я ошибаюсь здесь), переменные класса разделяются между классами, экземплярами этих классов, подклассами и экземплярами подклассов. Однако для подклассов и их экземпляров переменная класса делится только с суперклассом, если переменной класса присвоено суперкласс. Если он не был назначен в суперклассе, он остается неопределенным там до тех пор, пока ему не будет присвоено значение, после чего переменная станет общедоступной ... Что за черт? (См. Пример ниже, если вы запутались.)
Так почему же это? Я где-то слышал, что переменные класса Ruby основаны на некоторой аналогичной концепции в Smalltalk, но я действительно вне себя от того, почему такое поведение было бы желательно.
Пример:
В superfoo_and_subbar.rb
:
class SuperFoo
def class_var_x=(x)
@@x = x
end
def class_var_x
@@x
end
end
class SubBar < SuperFoo
# Define these again, just in case they're bound at compile time or something...
def class_var_x=(x)
@@x = x
end
def class_var_x
@@x
end
end
В PRY
(или) IRB
сессии:
# Okay, let's do this!
require './superfoo_and_subbar' # => true
SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo
SubBar.new.class_var_x # NameError: uninitialized class variable @@x in SubBar
# Okay, no suprise there, let's define the class variable on SuperFoo
SuperFoo.new.class_var_x = 1
SuperFoo.new.class_var_x # => 1
# Okay, looks about right. What does bar say now?
SubBar.new.class_var_x # => 1
# Okay, that's pretty weird but I did hear that Ruby class variables behave
# that way, so no big deal.
SubBar.new.class_var_x = 2
SubBar.new.class_var_x # => 2
SuperFoo.new.class_var_x # => 2
# Right, so these both point to the same variable then.
Новая Поддеть сессия:
# Now let's try this again:
require './superfoo_and_subbar' # => true
SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo
SubBar.new.class_var_x # NameError: uninitialized class variable @@x in SubBar
# So far so good. Let's set x on SubBar first this time
SubBar.new.class_var_x = 2
SubBar.new.class_var_x # => 2
# Okay, so x is now set on SubBar so it should also be set on SuperFoo then, right?
SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo
# Wait, what? So they're seperate variables now?
SubBar.new.class_var_x # => 2
SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo
# It certainly looks that way. What happens if I set x on SuperFoo then?
SuperFoo.new.class_var_x = 3
SuperFoo.new.class_var_x # => 3
SubBar.new.class_var_x # => 3
# Wait, so now they're the same varaible again? What the heck?
SubBar.new.class_var_x = 4
SuperFoo.new.class_var_x # => 4
SubBar.new.class_var_x # => 4
# ...WHY!?!? Seriously, what's the point of this?
Это ведет себя так из-за переписанных методов в SubBar.Если он был подклассифицирован как обычный: 'SubBar
steenslag
@steenslag Я бы не сказал «как бы ожидал», потому что, честно говоря, мой первый инстинкт был бы для переменных класса просто синтаксическим сахаром, например переменными класса. Вы правы, хотя не переопределяя эти два метода, мой второй пример ведет себя как первый. – Ajedi32
@Arup Я просто заметил, что удивительное поведение не происходит, когда переопределенные методы удаляются из кода. (Пример David A Black меня совсем не удивил! Беспокойство ...) – steenslag