我有机会在StackOverflow中查看并发现了同样的问题,我试图从Ruby Koans(Ruby Koans: explicit scoping on a class definition part 2)中更好地理解。
class MyAnimals
LEGS = 2
class Bird < Animal
def legs_in_bird
LEGS
end
end
end
def test_who_wins_with_both_nested_and_inherited_constants
assert_equal 2, MyAnimals::Bird.new.legs_in_bird
end
# QUESTION: Which has precedence: The constant in the lexical scope,
# or the constant from the inheritance heirarachy?
# ------------------------------------------------------------------
class MyAnimals::Oyster < Animal
def legs_in_oyster
LEGS
end
end
def test_who_wins_with_explicit_scoping_on_class_definition
assert_equal 4, MyAnimals::Oyster.new.legs_in_oyster
end
# QUESTION: Now Which has precedence: The constant in the lexical
# scope, or the constant from the inheritance heirarachy? Why is it
# different than the previous answer?
根据链接中的解释,似乎其他人(包括我自己)的主要困惑是因为类的定义:
class MyAnimals::Oyster < Animal
# stuff goes in here
end
我最初的想法是MyAnimals :: Oyster意味着Oyster类是在MyAnimals中定义的。换句话说,我认为上面的代码类似于以下代码:
class MyAnimals
class Oyster < Animal
# stuff goes in here
end
end
为了测试我的想法,我在IRB中做了以下事情:
class MyAnimals
LEGS = 2
class Bird < Animal
def legs_in_bird
LEGS
end
end
end
class MyAnimals::Oyster # You should notice that I'm not inheriting from Animal anymore
def legs_in_oyster
LEGS
end
end
如果我的推理是正确的,那么我希望下面的代码返回2
MyAnimals::Oyster.new.legs_in_oyster # => NameError: uninitialized constant MyAnimals::Oyster::LEGS
由于这不会返回2,有人可以向我解释为什么它不返回2?
编辑: 我忽略了添加Animal类;这是:
class Animal
LEGS = 4
def legs_in_animal
LEGS
end
class NestedAnimal
def legs_in_nested_animal
LEGS
end
end
end
答案 0 :(得分:4)
回到Ruby搜索值的顺序。第1步是“封闭范围”。当您定义像这样的嵌套类
时class Animal
class Bird
end
end
'Bird'包含在'Animal'范围内。
当你这样做时
class Animal
end
class Animal::Bird
end
虽然您已将'Bird'定义为嵌套在'Animal'中,但在您定义'Bird'时,封闭范围是 全局 范围。返回IRB并定义LEGS = 0
(在全局范围内)并再次尝试您的Oyster。
答案 1 :(得分:1)
在严格的词汇/静态范围的语言(如C ++或Java)中,只需检查当前范围,向上提升一个范围并重复直到达到最大范围,即可解析标识符。例如:如果您的示例是C ++,则首先在调用类Bird / Oyster中搜索LEGS,然后在Animal中搜索,然后在My Animal中搜索。
Ruby有一种动态范围。每个对象,即使它位于相同的“位置”,也可以具有自己的范围查找顺序,具体取决于它在运行时的定义或创建方式。您可以将每个方法视为具有要搜索的作用域堆栈,以及如何定义将新作用域推送到该堆栈。
由于定义了Bird的方式(BaseScope不是它的真名,你没有提供足够的代码来提供这个)BaseScope::MyAnimals::Bird
,BaseScope::MyAnimals::Animal
,BaseScope::MyAnimals
和{{ 1}}搜索解析名称。
虽然第一个Oyster只获得BaseScope
,BaseScope::MyAnimals::Oyster
和BaseScope::MyAnimals::Animal
。
没有继承的牡蛎变得更少,只有BaseScope
和BaseScope::MyAnimals::Oyster
。
在此示例中,每次使用class关键字和继承都会推动另一个范围检查其搜索内容的作用域堆栈。所以使用BaseScope
只将一个条目推入此堆栈。
为简单起见,我省略了方法legs_in_oyster。这是一个可以搜索的范围。它的微不足道的定义是自我解释的,包括它会为这个答案添加许多无用的文本。
我也忽略了全球范围的简单性。我知道Koans在BaseScope和全球范围内至少有一个范围。