在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将查看打开的模块定义以查找常量Foo
: M::N
,然后是M
,最后是Object
。 最顶层打开的模块定义始终是Object
。
所以M::N.const_get :Foo
等同于在打开的类只有M::N
和Object
时访问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