2

在 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 上运行时得到了相同的结果。

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

4

1 回答 1

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,要么使用某种机制来使用类的实例变量。说:

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
于 2012-04-11T18:11:15.033 回答