使用 class_eval 和 instance_eval 访问 Ruby 类变量
我有以下内容:
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
块访问类变量。
I have the following:
class Test
@@a = 10
def show_a()
puts "a: #{@@a}"
end
class << self
@@b = '40'
def show_b
puts "b: #{@@b}"
end
end
end
Why does following work:
Test.instance_eval{show_b}
b: 40
=> nil
But I can't access @@b
directly?
Test.instance_eval{ @@b }
NameError: uninitialized class variable @@b in Object
Likewise, the following works
t = Test.new
t.instance_eval{show_a}
a: 10
=> nil
but the following fails
t.instance_eval{ @@a }
NameError: uninitialized class variable @@a in Object
I don't understand why I can't access the Class Variables directly from the instance_eval
blocks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我刚刚在 RubyKaigi 聚会上向 Matz 提出了同样的问题。我已经喝得半醉了,但他却完全清醒,所以你可以将此作为确定的答案。
Anton 是对的 - 无法通过 instance_eval() 访问类变量的原因是“只是因为”。甚至 class_eval() 也有同样的问题(Matz 本人并不完全确定 class_eval() ,直到我告诉他我已经尝试过了)。更具体地说:就作用域而言,类变量更像常量而不是实例变量,因此在访问它们时切换 self(如 instance_eval() 和 class_eval() 所做的那样)不会产生任何差异。
一般来说,完全避免类变量可能是个好主意。
I just asked the same question to Matz during the RubyKaigi party. I was half-drunk, but he was perfectly sober, so you can take this as the definitive answer.
Anton is right - the reason why you cannot access class variables through instance_eval() is "just because". Even class_eval() shares the same issue (Matz himself wasn't totally sure about class_eval() until I told him I'd already tried it). More specifically: scope-wise, class variables are more like constants than instance variables, so switching self (as instance_eval() and class_eval() do) is not going to make any difference when it comes to accessing them.
In general, it might be a good idea to avoid class variables altogether.
编辑:下面的代码在 1.8.7 和 1.9.1 上进行了测试...似乎 1.9.2 的情况又有所不同:/
情况实际上并不是那么简单。根据您使用的是 1.8 还是 1.9 以及您使用的是
class_eval
还是instance_eval
,行为会有所不同。下面的示例详细介绍了大多数情况下的行为。
为了更好的衡量,我还包括了常量的行为,因为它们的行为类似于类变量,但不完全相同。
Ruby 1.8 中的类变量
class_eval
:Ruby 1.9 中的
class_eval
:所以类变量是 使用
class_eval
instance_eval
时在 1.9 中查找(但不在 1.8 中)>在 Ruby 1.8 和 1.9 中
出现使用
instance_eval
时,类变量在 1.8 或 1.9 中不查找。常量的情况也很有趣。 :
常量
class_eval
in Ruby 1.8class_eval
in Ruby 1.9因此,与类变量一样,常量在以下位置查找1.9,但在 1.8 中不,对于
Ruby 1.8 中的
class_eval
instance_eval
instance_eval
在 Ruby 1.9 中看来常量查找与 Ruby 1.9 中的类变量查找不太相似。
Hello
实例确实可以访问常量,而Hello
类则不能。EDIT: below code was tested with 1.8.7 and 1.9.1...it seems the situation is different again with 1.9.2 :/
The situation actually isn't that straight forward. There are differences in behaviour depending on whether you're using 1.8 or 1.9 and whether you're using
class_eval
orinstance_eval
.The examples below detail the behaviour in most situations.
I also included the behaviour of constants, for good measure, as their behaviour is similar to, but not exactly the same as, class variables.
Class variables
class_eval
in Ruby 1.8:class_eval
in Ruby 1.9:So class variables are looked up in 1.9 (but not in 1.8) when using
class_eval
instance_eval
in Ruby 1.8 and 1.9It appears class variables are not looked up in 1.8 or 1.9 when using
instance_eval
What is also interesting is the case for constants:
Constants
class_eval
in Ruby 1.8class_eval
in Ruby 1.9So, as with class variables, constants are looked up in 1.9 but not in 1.8 for
class_eval
instance_eval
in Ruby 1.8instance_eval
in Ruby 1.9It appears that constant lookup is not quite analogous to class variable look up for Ruby 1.9. A
Hello
instance does get access to the constant while theHello
class does not.好吧,可能最好的答案是“只是因为”:简而言之,instance_eval 创建了某种通过给定对象的绑定来调用的单例过程。我同意这听起来有点奇怪,但事实就是如此。
如果您使用字符串执行instance_eval,您甚至会收到一条警告,指出您的方法试图访问类变量:
Well, probably the best answer is "just because": the instance_eval in a nutshell creates some kind of singleton proc that is invoked with the binding of a given object. I agree that is sounds a bit strange, but it is what it is.
If you execute instance_eval with a string, you will even get a warning that your method tries to access class variable:
Ruby 2.1
这是我发现的访问类变量的最简洁且语义正确的方法:
Ruby 2.1
This is the most concise and semantically correct way I've found to access a class variable: