在Ruby中混淆const_get的行为?

时间:2010-07-02 12:01:57

标签: ruby

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

我也知道默认情况下const_get可能会查找接收者的继承链。以下是有效的:

class A; HELLO = :hello; end
class B < A; end
B.const_get(:HELLO) #=> :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

由于

3 个答案:

答案 0 :(得分:11)

你是混淆的正确...文档没有说明Ruby在Modules中查找常量并且已被修改to state this explicitly。如果在常规层次结构中未找到常量,则Ruby会从Object重新开始查找,可能是found in the source

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

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

但是,在这两个地方,访问ObjectArray等常量都很好(感谢上帝!)。发生的事情是Ruby维护着一个“打开的模块定义”列表。如果常量具有明确的范围,例如LookHereOnly::Foo,则 LookHereOnly及其包含的模块将被搜索。如果未指定范围(如上例中的Foo),Ruby将查看打开的模块定义以查找常量FooM::N,然后M,最后Object。最顶层打开的模块定义始终为Object

所以M::N.const_get :Foo相当于在打开的课程只有FooM::N时访问Object,就像我示例的最后一部分一样。

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:0)

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

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