在 Ruby 中,当使用 class_exec
定义类的内容时,我得到了意外的结果。当我在发送到 class_exec
的 block 中定义类变量时,该类变量是在 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吗?
最佳答案
类变量在编译时绑定(bind)到声明它们的类。传递给 class_exec
的 block 在传递给 class_exec
之前进行编译,因此类变量绑定(bind)到 Object
。
我猜你的 class_exec 位于顶层,位于 Object 中,所以这就是它们所在的位置。演示:
public
class Object
@@x = "ribbit"
end
def foo
puts "test: #{@@x}"
end
x = Object.new
x.foo
这就是为什么当您在模块中使用类变量时,包含该模块的所有类(通过包含的方法)将看到相同的类变量。类变量绑定(bind)到模块。如果你运行这个:
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
关于Ruby:定义类变量时 class_exec 出现意外结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10109925/