Ruby:定义类变量时class_exec的意外结果

在Ruby中,当使用class_exec定义类的内容时,我得到了意想不到的结果。 当我在发送到class_exec的块中定义一个类变量时,类变量是在Object上定义的,而不是在调用class_exec的类上定义的:

 class X; end X.class_exec do @@inner_value = "123" def inner_value @@inner_value end def inner_value=(arg) @@inner_value = arg end end obj1 = X.new puts obj1.inner_value puts @@inner_value puts Object.class_variables 

生产:

 123 123 @@inner_value 

使用class_eval时不会发生这种情况:

 X.class_eval(<<-RUBY) @@inner_value = "123" def inner_value @@inner_value end def inner_value=(arg) @@inner_value = arg end RUBY obj1 = X.new puts obj1.inner_value puts @@inner_value puts Object.class_variables 

生产:

 123 

和一个错误:

 uninitialized class variable @@inner_value in Object (NameError) 

class_eval的结果是我在两种情况下都会发生的结果。 我已经尝试过MRI 1.8.7和MRI 1.9.3,并在Windows XP上运行相同的结果。

这是预期的行为吗? 如果是这样,为什么? 如果没有,bug?

类变量绑定到在编译时声明它们的类。 传递给class_exec的块在传递给class_exec进行编译,因此类变量绑定到Object

我猜你的class_exec是在顶层,它在Object中,所以它就是它们的去向。 展示:

 public class Object @@x = "ribbit" end def foo puts "test: #{@@x}" end x = Object.new x.foo 

这就是为什么当你在模块中使用类变量时,包含该模块的所有类(通过包含的方法)将看到相同的类变量。 类变量绑定到模块。 如果你运行这个:

 class WithClassVars def self.classvars @classvars ||= {} end def classvars self.class.classvars end end class A < WithClassVars;end class B < WithClassVars;end a = A.new b = B.new a.classvars[:a] = 1 b.classvars[:a] = 2 puts a.classvars puts b.classvars 

a和b将以相同的数据结束。

如果将代码作为字符串传递给class_eval ,则字符串将 class_eval编译,因此您可以确保它们位于正确的类中。

因此,如果要存储每类数据,则必须使用class_eval,或使用某种机制来使用类的实例变量。 说:

 class WithClassVars def self.classvars @classvars ||= {} end def classvars self.class.classvars end end class A < WithClassVars;end class B < WithClassVars;end a = A.new b = B.new a.classvars[:a] = 1 b.classvars[:a] = 2 puts a.classvars puts b.classvars