Ruby:当使用快捷符号' class A::B '时常量查找是如何工作的



当在一个模块中定义的类中工作时,我想访问在同一模块中定义的另一个类。

当使用这个代码

module SomeModule
class Foo
def self.get_bar
Bar
end
end
end
module SomeModule
class Bar
end
end
# works and returns SomeModule::Bar
SomeModule::Foo.get_bar

SomeModule::Foo查找Bar有效。在局部作用域SomeModule::Foo中没有找到它,所以它向上查找SomeModule并找到SomeModule::Bar

但是当使用快捷符号class A::B来定义类时,查找不再工作:

module SomeModule
end
class SomeModule::Foo
def self.get_bar
Bar
end
end
class SomeModule::Bar
end
# does not work and raises a NameError
SomeModule::Foo.get_bar

产生错误NameError: uninitialized constant SomeModule::Foo::Bar。但对我来说,这两个代码看起来是一样的,应该产生相同的输出。我显然在这里遗漏了一个关键概念。

有人能解释一下为什么查找在一种情况下有效而在另一种情况下无效吗?是否有可能通过自省类提前知道查找是否有效?

有一篇很棒的文章详细解释了它是如何工作的。总结一下,Ruby的常量查找是基于词法作用域的。

有一个方法Module.nesting,它返回一个常量数组,Ruby首先寻找所需的const。

module SomeModule
class Buzz
def self.get_bar
p Module.nesting #=> [SomeModule::Buzz, SomeModule]
Bar
end
end
end
class SomeModule::Foo
def self.get_bar
p Module.nesting #=> [SomeModule::Foo]
Bar rescue puts "Oops, can not find it"
end
end
class SomeModule::Bar
end
SomeModule::Buzz.get_bar
SomeModule::Foo.get_bar

Ruby常量查找是基于词法作用域的,这两个例子在这方面有很大的不同。看一下:

module SomeModule
puts Module.nesting.inspect #=> [SomeModule]
puts Module.nesting.map(&:constants).inspect # => [[]], we didn't define Foo yet

class Foo
puts Module.nesting.inspect #=> [SomeModule::Foo, SomeModule]
puts Module.nesting.map(&:constants).inspect #=> [[], [:Foo]], at this point SomeModule is already "aware" of Foo
def self.get_bar
Bar
end
end
end
module SomeModule
puts Module.nesting.inspect #=> [SomeModule]
puts Module.nesting.map(&:constants).inspect #=> [[:Foo]], we didn't define Bar yet

class Bar
puts Module.nesting.inspect #=> [SomeModule::Bar, SomeModule]
puts Module.nesting.map(&:constants).inspect #=> [[], [:Foo, :Bar]]
end
end

和第二个

module SomeModule
puts Module.nesting.inspect #=> [SomeModule]
puts Module.nesting.map(&:constants).inspect #=> [[]]
end
class SomeModule::Foo
puts Module.nesting.inspect #=> [SomeModule:Foo]
puts Module.nesting.map(&:constants).inspect #=> [[]]

def self.get_bar
Bar
end
end
class SomeModule::Bar
puts Module.nesting.inspect #=> [SomeModule:Bar]
puts Module.nesting.map(&:constants).inspect #=> [[]]
end

如你所见,第一种和第二种情况下的Bar在完全不同的词法范围内被解析,从而导致完全不同的结果。

关于你的问题

是否有可能通过内省类来提前知道查找是成功还是失败?

对于孤立的代码片段是可能的,但在应用程序范围内,我不会依赖于它。当有疑问时,只需显式指定嵌套从最外层的上下文(::SomeModule::Bar)…

最新更新