Ruby:对类定义的显式范围

时间:2011-01-07 16:29:28

标签: ruby class

免责声明:代码取自ruby koans

这是对类中常量范围的讨论。以下是几个班级的定义:

class Animal
  LEGS = 4
  def legs_in_animal
    LEGS
  end
end

class MyAnimals
  LEGS = 2

  class Bird < Animal
    def legs_in_bird
      LEGS
    end
  end
end

此时执行MyAnimals::Bird.new.legs_in_bird导致2并且我理解为什么 - 在继承层次之前搜索常量的词法空间。

然后定义了这个类:

class MyAnimals::Oyster < Animal
  def legs_in_oyster
    LEGS
  end
end

教程说现在调用MyAnimals::Oyster.new.legs_in_oyster导致4,我无法弄明白。在我看来,Oyster是MyAnimals中的嵌套类,因此我希望它的行为与Birds类在上面的行为相同。我遗漏了一些关于用明确的范围界定声明类Oyster的关键信息。

任何人都可以向我解释这个吗?我已经通过谷歌发现了数百个ruby课程教程,但没有一个能解决这种情况。

提前谢谢你......

2 个答案:

答案 0 :(得分:22)

我认为this example解释得最好。 Ruby按此顺序搜索常量定义:

  1. 封闭范围
  2. 任何外部范围(重复到达顶级)任何外部范围(最多但不包括顶级
  3. 包含的模块
  4. 超类(ES)
  5. 顶级
  6. 对象
  7. 内核
  8. 修改

    感谢Mark Amery指出此错误。只有在没有封闭范围和/或超类的情况下才能达到顶级。链接的例子实际上清楚地表明了这一点,遗憾的是我读错了。

    这种情况的一个例子:

    FOO = 'I pity the foo!'
    
    module One
      FOO = 'one'
    
      class Two
        FOO = 'two'
    
        def self.foo
          FOO
        end
      end
    
      class Three < Two
        def self.foo
          FOO
        end
      end
    end
    
    class Four
      class Five < Four
        def self.foo
          FOO
        end
      end
    end
    
    describe FOO do
      it "depends where it is defined" do
        expect(FOO).to eq 'I pity the foo!' # top-level
        expect(One::FOO).to eq 'one' # module
        expect(One::Two.foo).to eq 'two' # class
        expect(One::Three.foo).to eq 'one' # outer scope (One) comes before superclass
        expect(Four::Five.foo).to eq 'I pity the foo!' # top-level
      end
    end
    

答案 1 :(得分:5)

如果你定义Oyster INSIDE MyAnimals类定义,那么你得到leg_in_oyster为2的答案。

如果您单独定义Oyster - 也就是说,在LEGS = 2超出范围后定义它,则会得到4的响应。

这告诉我,嵌套类的行为与命名空间的行为不同,可能更像是一个闭包。

--- --- EDIT

irb(main):076:0> class MyAnimals::RunningRoach < Animal; def using_legs; LEGS; end; end
=> nil
irb(main):077:0> MyAnimals::RunningRoach.new.kind_of?(MyAnimals)
=> false
irb(main):078:0> MyAnimals::RunningRoach.new.kind_of?(Animal)
=> true
irb(main):081:0> class MyAnimals::Mantis < MyAnimals; def killing_legs; LEGS; end; end
=> nil
irb(main):082:0> MyAnimals::Mantis.new.kind_of?(Animal)
=> false
irb(main):083:0> MyAnimals::Mantis.new.kind_of?(MyAnimals)
=> true
irb(main):084:0> MyAnimals::Mantis.new.killing_legs
=> 2
irb(main):085:0> MyAnimals::RunningRoach.new.using_legs
=> 4

根据“Ruby编程语言”,常量会在首先使用它们的地方的词法范围中查找,然后在继承层次结构中查找。那么继承Animal的词汇范围是什么?动物本身吧? MyAnimals类重新定义了LEGS,因此任何使用LEGS并且在 MyAnimals内定义的东西都将首先在MyAnimals中查找LEGS。