为什么我不能从“A”访问下面的“B”,但可以从主环境访问?
module A; end
A.instance_eval{B=1}
B #=> 1
A::B #=> uninitialized
从(强调我的)的文档中:instance_eval
在接收器 (obj) 的上下文中评估包含 Ruby 源代码或给定块的字符串。为了设置上下文,在代码执行时将变量 self 设置为 obj,从而使代码可以访问 obj 的实例变量。
这里没有做更多的事情。特别是,常量赋值在块的封闭上下文中运行。观察:
irb(main):001:0> module A
irb(main):002:1> module B; end
irb(main):003:1> B.instance_eval { C = 1 }
irb(main):004:1> end
=> 1
irb(main):006:0> A::C
=> 1
. . . 未在类或模块中定义的常量被赋予全局范围。
常量定义重要的是封闭的词法范围,而不是当前的接收者或self
.
这样做的惯用方法是
A.const_set(:B, 1)
A::B #=> 1
至于为什么它不起作用,在 Ruby 1.8 和 1.9.2+ 中(在 1.9.1 中有所不同),常量查找是词法范围的。我找到了一篇很好的博客文章,里面有解释。去引用:
请注意,这些规则适用于常量定义和查找。在 1.8 和 1.9.2 中,定义在 class_evaluate 块中的常量将定义在封闭的词法范围内,而不是接收方的范围内。
对于instance_eval
.
module A; end
A.class_eval{B=1}
B # Undefined
A::B # 1
至于它为什么起作用,我不太确定。在创建诸如 Small Eigen Collider 之类的元框架时,我偶尔会使用这样的元编程,但不会在日常工作中使用。