使用class_eval和instance_eval访问红宝石类变量
-
26-09-2019 - |
题
我有以下内容:
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
块。
解决方案
在Rubykaigi派对上,我只是向Matz问了同样的问题。我半滴了,但他非常清醒,所以您可以将其作为确定的答案。
Anton是正确的 - 您无法通过instance_eval()访问类变量的原因是“只是因为”。甚至class_eval()也共享了相同的问题(MATZ本人对Class_eval()不确定,直到我告诉他我已经尝试过)。更具体地说:在范围方面,类变量比实例变量更像常数,因此切换自我(如instance_eval()和class_eval()do)在访问它们时不会有任何区别。
通常,避免完全避免班级变量可能是一个好主意。
其他提示
编辑: 下面的代码用1.8.7和1.9.1进行了测试...似乎以1.9.2:/的方式再次不同。
情况实际上并不是那么直接。行为有差异,具体取决于您使用的是1.8还是1.9以及是否正在使用 class_eval
或者 instance_eval
.
下面的示例详细介绍了大多数情况下的行为。
我还将常数的行为包括在内,因为它们的行为与阶级变量相似,但并不完全相同。
类变量
class_eval
在红宝石1.8中:
class Hello
@@foo = :foo
end
Hello.class_eval { @@foo } #=> uninitialized class variable
class_eval
在红宝石1.9中:
Hello.class_eval { @@foo } #=> :foo
所以类变量 是 使用时以1.9(但不在1.8中)抬头 class_eval
instance_eval
在红宝石1.8中 和 1.9
Hello.instance_eval { @@foo } #=> uninitialized class variable
Hello.new.instance_eval { @@foo } #=> uninitialized class variable
看起来类变量是 不是 在1.8中抬头 或者 1.9使用时 instance_eval
有趣的是 常数:
常数
class_eval
在红宝石1.8中
class Hello
Foo = :foo
end
Hello.class_eval { Foo } #=> uninitialized constant
class_eval
在红宝石1.9中
Hello.class_eval { Foo } #=> :foo
因此,与班级变量一样,常数在1.9中被查找,但是 不是 在1.8中 class_eval
instance_eval
在红宝石1.8中
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> uninitialized constant
instance_eval
在红宝石1.9中
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> :foo
看来恒定查找并不类似于类变量查找Ruby 1.9。一个 Hello
实例 做 访问常数 Hello
班级没有。
好吧,最好的答案可能是“仅仅是因为”:简而言之的instance_eval创建了某种单身人士proc,并通过给定对象的绑定来调用。我同意这听起来有些奇怪,但这就是事实。
如果您使用字符串执行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 ...
红宝石2.1
这是我发现访问类变量的最简洁和语义上最正确的方法:
class Hello
@@foo = :foo_value
end
Hello.class_variable_get :@@foo #=> :foo_value