在Ruby中混淆const_get的行为?

根据文档mod.const_get(sym) “返回mod中命名常量的值。”

我也知道const_get默认情况下可以查找接收器的inheritance链。 以下是有效的:

 class A; HELLO = :hello; end class B  :hello 

我也知道Ruby中的类是子类Object ,因此即使接收器是普通类,也可以使用const_get查找“全局”常量:

 class C; end C.const_get(:Array) #=> Array 

然而,这是我感到困惑的地方 – 模块不是Object子类。 那么为什么我仍然可以使用const_get从模块中查找“全局”常量? 为什么以下工作?

 module M; end M.const_get(:Array) #=> Array 

如果文档是正确的 – const_get只是查找在接收者或其超类下定义的常量。 但是在上面的代码中, Object不是M的超类,为什么可以查找Array呢?

谢谢

你应该感到困惑是正确的…文档没有说明Ruby在查找Modules的常量时会有特殊情况,并且已被修改为明确说明 。 如果在常规层次结构中未找到常量,则Ruby会重新启动Object的查找,这可以在源代码中找到 。

常量查找本身可能有点令人困惑。 采用以下示例:

 module M Foo = :bar module N # Accessing Foo here is fine: p Foo # => bar end end module M::N # Accessing Foo here isn't p Foo # => uninitialized constant M::N::Foo end p M::N.const_get :Foo # => uninitialized constant M::N::Foo 

但是,在这两个地方,访问像Array这样的Object级常量很好(感谢上帝!)。 发生了什么,Ruby维护着一个“打开的模块定义”列表。 如果常量具有显式范围,例如LookHereOnly::Foo则只搜索 LookHereOnly及其包含的模块。 如果没有指定范围(如上例中的Foo ),Ruby将查看打开的模块定义以查找常量FooM::N ,然后是M ,最后是Object 。 最顶层打开的模块定义始终是Object

所以M::N.const_get :Foo等同于在打开的类只有M::NObject时访问Foo ,就像我的例子的最后一部分一样。

我希望我能做到这一点,因为我仍然对自己不断的查询感到困惑:-)

我想出了以下脚本来加载名称间隔常量:

 def load_constant(name) parts = name.split('::') klass = Module.const_get(parts.shift) klass = klass.const_get(parts.shift) until parts.empty? klass end 

只要我们不检查错误,您可以:

 def load_constant(name) name.split('::').inject(Module) do |mod_path, mod_to_find| mod_path.const_get(mod_to_find) end end