为什么Ruby类变量的行为如此奇怪?

为什么Ruby类变量的行为如此奇怪?,ruby,Ruby,我只是花了很多时间在IRB(实际上,PRY)上玩,试图弄清楚类变量在Ruby中是如何工作的,我完全被我的发现所迷惑 从我所看到的(如果我错了,请纠正我),类变量在类、这些类的实例、子类和子类的实例之间共享。但是,对于子类及其实例,如果类变量已分配给超类,则类变量仅共享给超类。如果它在超类中没有被赋值,那么它在超类中保持未定义状态,直到它被赋值到该变量成为共享的点为止。。。搞什么鬼?(如果您感到困惑,请参见下面的示例。) 为什么会这样?我在某处听说Ruby类变量基于Smalltalk中类似的概念,

我只是花了很多时间在
IRB
(实际上,
PRY
)上玩,试图弄清楚类变量在Ruby中是如何工作的,我完全被我的发现所迷惑

从我所看到的(如果我错了,请纠正我),类变量在类、这些类的实例、子类和子类的实例之间共享。但是,对于子类及其实例,如果类变量已分配给超类,则类变量仅共享给超类。如果它在超类中没有被赋值,那么它在超类中保持未定义状态,直到它被赋值到该变量成为共享的点为止。。。搞什么鬼?(如果您感到困惑,请参见下面的示例。)

为什么会这样?我在某处听说Ruby类变量基于Smalltalk中类似的概念,但我真的不明白为什么需要这种行为


示例:

superfoo\u和\u 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会议:

# 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.new.class\u var\u x
仅在class
SubBar
中创建class变量。所以很明显,它的超级类将无法访问它

class Foo
  def x_class_var=(val)
    @@x =val
  end
  def x_class_var
    @@x
  end
end

class Bar<Foo
  def x_class_var=(val)
    @@x =val
  end
  def x_class_var
    @@x
  end
end

Foo.class_variables # => []
Bar.class_variables # => []
Bar.new.x_class_var = 10
Foo.class_variables # => []
Bar.class_variables # => [:@@x]

现在
@@x
是在
Foo
类中创建的,因此作为
Foo
的子类,
Bar
可以访问
@@x
。这是因为类变量可以从超类到子类共享,而不是从子类到超类共享,正如我在上面的示例中所示。

在第一次PRY会话中,当您选中
SubBar.new.class\u var\u x
时,它会检查
@@class\u var\u x
是否存在。由于它确实存在于
SuperFoo
SubBar
中,因此需要
SuperFoo
@@class\u var\u x


在第二个会话中,当您选中
SuperFoo.new.class\u var\u x
时,它没有查看
SubBar
@@class\u var\u x
,因为父类的对象不关心从它们继承的类,因此它创建了一个新的
@@class\u var\u x

实例,但随后我分配给
SuperFoo.new.class\u var\u x
,突然
SubBar.new.class\u var\u x
引用了
SuperFoo
中的值,而不是我以前分配给它的值?谢谢,这是一个关于类变量如何工作的非常好的心理模型。实际上,对于这个问题,我更关心的是“为什么”,而不是“如何”。(例如,Ruby为什么会这样做?有没有人会希望以这种方式使用类变量的原因?)+1:它的行为与此类似,因为SubBar中的方法被覆盖。如果它像往常一样被子类化:
SubBar
,它的行为将像您预期的那样。@steenslag我不会说“像我预期的那样”,因为老实说,我的第一直觉是类变量只是类上实例变量的语法糖。您是对的,尽管不重写这两个方法会使我的第二个示例的行为更像第一个。@Arup我刚刚观察到,当从代码中删除被重写的方法时,不会出现令人惊讶的行为。(大卫一个黑色的例子一点也不让我惊讶!令人担忧……)
class Foo
  def x_class_var=(val)
    @@x =val
  end
  def x_class_var
    @@x
  end
end

class Bar<Foo
  def x_class_var=(val)
    @@x =val
  end
  def x_class_var
    @@x
  end
end

Foo.class_variables # => []
Bar.class_variables # => []
Bar.new.x_class_var = 10
Foo.class_variables # => []
Bar.class_variables # => [:@@x]
Foo.class_variables # => []
Bar.class_variables # => []
Foo.new.x_class_var = 10
Foo.class_variables # => [:@@x]
Bar.class_variables # => [:@@x]