2012-04-11 58 views
2

在Ruby中,当定义类为class_exec的内容时,我得到了意想不到的结果。当我发送到class_exec块定义一个类变量,类变量正在对Object代替其上class_exec被称为类定义:Ruby:定义类变量时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上运行的结果相同。

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

回答

2

类变量绑定到它们在编译时处声明的类别。传递给class_exec的块在传递给class_exec之前会被编译,所以类变量绑定到Object

我想你的class_exec是在顶层,这是在对象,所以这就是他们去的地方。为了证明:

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