在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?
答案 0 :(得分:2)
类变量绑定到在编译时声明的类。传递给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
中编译,因此您可以确保它们位于正确的类中。< / p>
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