23

我有以下内容:

class Test
    @@a = 10

    def show_a()
        puts "a: #{@@a}"
    end

    class << self
      @@b = '40'

      def show_b
        puts "b: #{@@b}"
    end
  end
end

为什么以下工作:

Test.instance_eval{show_b}
b: 40
=> nil

但是我不能@@b直接访问?

Test.instance_eval{ @@b }
NameError: uninitialized class variable @@b in Object

同样,以下作品

t = Test.new
t.instance_eval{show_a}
a: 10
=> nil

但以下失败

t.instance_eval{ @@a }
NameError: uninitialized class variable @@a in Object

我不明白为什么我不能直接从instance_eval块中访问类变量。

4

4 回答 4

18

我刚刚在 RubyKaigi 派对上向 Matz 提出了同样的问题。我喝得半醉,但他完全清醒,所以你可以把这个作为确定的答案。

Anton 是对的 - 您无法通过 instance_eval() 访问类变量的原因是“只是因为”。甚至 class_eval() 也有同样的问题(Matz 本人并不完全确定 class_eval() 直到我告诉他我已经尝试过了)。更具体地说:在范围方面,类变量更像是常量而不是实例变量,因此在访问它们时切换 self(如 instance_eval() 和 class_eval() 所做的)不会有任何区别。

一般来说,完全避免类变量可能是个好主意。

于 2010-08-27T12:38:20.240 回答
16

编辑:下面的代码用 1.8.7 和 1.9.1 进行了测试......似乎情况再次与 1.9.2 不同:/

情况实际上并没有那么简单。根据您使用的是 1.8 还是 1.9 以及您使用的是class_eval还是instance_eval.

下面的示例详细说明了大多数情况下的行为。

为了更好地衡量,我还包括了常量的行为,因为它们的行为类似于类变量,但不完全相同。

类变量

class_eval在 Ruby 1.8 中:

class Hello
    @@foo = :foo
end

Hello.class_eval { @@foo } #=> uninitialized class variable

class_eval在 Ruby 1.9 中:

Hello.class_eval { @@foo } #=> :foo

所以类变量在使用时在 1.9(但不在 1.8)查找class_eval

instance_eval在 Ruby 1.81.9中

Hello.instance_eval { @@foo } #=> uninitialized class variable
Hello.new.instance_eval { @@foo } #=> uninitialized class variable

使用时似乎没有在 1.81.9中查找类变量instance_eval

同样有趣的是常量的情况:

常数

class_eval在 Ruby 1.8 中

class Hello
    Foo = :foo
end

Hello.class_eval { Foo } #=> uninitialized constant

class_eval在 Ruby 1.9 中

Hello.class_eval { Foo } #=> :foo

因此,与类变量一样,常量在 1.9 中查找,但在 1.8 中不查找class_eval

instance_eval在 Ruby 1.8 中

Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> uninitialized constant

instance_eval在 Ruby 1.9 中

Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> :foo

看起来常量查找与 Ruby 1.9 的类变量查找不太相似。Hello实例确实可以访问常量,而Hello类则不能。

于 2010-08-28T11:19:18.047 回答
5

好吧,可能最好的答案是“仅仅因为”:简而言之,instance_eval 创建了某种单例过程,该过程通过给定对象的绑定来调用。我同意这听起来有点奇怪,但事实就是如此。

如果您使用字符串执行 instance_eval,您甚至会收到警告,提示您的方法尝试访问类变量:

irb(main):038:0> Test.new.instance_eval "@@a"
(eval):1: warning: class variable access from toplevel singleton method
NameError: (eval):1:in `irb_binding': uninitialized class variable ...
于 2010-08-12T08:10:29.127 回答
4

红宝石 2.1

这是我发现访问类变量的最简洁和语义正确的方法:

class Hello
    @@foo = :foo_value
end

Hello.class_variable_get :@@foo  #=> :foo_value
于 2014-03-14T18:01:58.017 回答