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

时间:2012-04-11 16:21:58

标签: ruby

在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?

1 个答案:

答案 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中编译,因此您可以确保它们位于正确的类中。< / p>

因此,如果要存储每类数据,则必须使用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