Ruby的Object#const_get如何实际工作?

时间:2016-06-28 21:09:12

标签: ruby

我最近发现Ruby(2.2.1)有一些有趣的"行为。

module Foo
  class Foo
  end
  class Bar
  end
end

Foo.const_get('Foo') #=> Foo::Foo
Foo.const_get('Bar') #=> Foo::Bar
Foo.const_get('Foo::Foo') #=> Foo
Foo.const_get('Foo::Bar') #=> NameError: uninitialized constant Foo::Foo::Bar
Foo.const_get('Foo::Foo::Bar') #=> Foo::Bar
Foo.const_get('Foo::Foo::Foo::Bar') #=> NameError: uninitialized constant Foo::Foo::Bar
Foo.const_get('Foo::Foo::Foo::Foo::Bar') #=> Foo::Bar
Foo.const_get('Foo::Foo::Foo') #=> Foo::Foo
Foo.const_get('Foo::Foo::Foo::Foo') #=> Foo
Foo.const_get('Foo::Foo::Foo::Foo::Foo') #=> Foo::Foo
Foo.const_get('Foo::Foo::Foo::Foo::Foo::Foo') #=> Foo

这有点令人惊讶。我的理解是const_get首先在接收器的常量集合中查找常量,然后查看Object的常量。好的。那么为什么上面的第四个Foo#const_get失败而第三个没有呢?

我也很好奇为什么在模块和课程之间调用Foo#const_get,这取决于你添加了多少::Foo

1 个答案:

答案 0 :(得分:11)

The docs say

  

如果提供了命名空间的类名,则此方法将以递归方式查找常量名称。

所以Foo.const_get('Foo::Bar')基本上与Foo.const_get('Foo').const_get('Bar')相同。使用这种解释,您的结果是有意义的。

你的第三个例子:

Foo.const_get('Foo::Foo')

相同
Foo.const_get('Foo').const_get('Foo')

第一个const_get查看顶级Foo(模块)中定义的常量,并查找嵌套类。所以整个事情实际上变成了:

Foo::Foo.const_get('Foo')

第二个调用然后查看该类,首先查看任何包含的常量(找不到),然后查看它的祖先。 Object是一个祖先,并且顶级Foo模块作为常量,因此找到并返回。

这也解释了添加额外::Foo时的更改。交替发生在查找嵌套类的顶级模块上的const_get和查找继承链并查找顶级模块的嵌套类之间。

还可以解释引发异常的第四个例子Foo.const_get('Foo::Bar')。它相当于

Foo.const_get('Foo').const_get('Bar')

第一部分Foo.const_get('Foo')与上面的情况相同,评估为Foo::Foo,所以整个事情现在实际上变成了:

Foo::Foo.const_get('Bar')

现在,嵌套的Foo类不包含Bar常量,并且查找继承链,Object也没有,因此结果为NameError