【问题标题】:Ruby classes, include, and scopeRuby 类、包含和作用域
【发布时间】:2011-09-26 00:37:22
【问题描述】:

包含模块如何影响范围?具体来说,在这个例子中:

module ModuleA
  class ClassA
    def initialize
      puts "test passed"
    end
  end
end

module ModuleB
  include ModuleA

  # test 1
  C = ClassA.new

  class ClassB
    def initialize
      c = ClassA.new
    end
  end
end

# test 2 and 3 fail without this
#include ModuleB

module ModuleC
  # this doesn't help
  include ModuleB

  # test 2
  ClassB.new

  # test 3
  ModuleB::ClassB.new
end

test 1 工作正常,但 test 2test 3 失败,没有注释掉 import ModuleB

  • 为什么ClassAModuleB(测试1)的范围内,但不在ClassB 范围内?
  • 为什么import ModuleBClassA 带入ClassB 的范围内?

【问题讨论】:

  • 要使 test2test3 工作,您需要在 ClassB 中包含 ModuleA 或制作 ClassB < ClassAsuper initialize.

标签: ruby module scope


【解决方案1】:

关键字 classmoduledef 是所谓的“范围门”。他们创建了新的范围。

#!/usr/bin/env ruby

module ModuleA
  class ClassA
    def initialize
      puts "test passed"
    end
  end
end

module ModuleB
  include ModuleA

  # test 1
  c = ClassA.new  # this works as ModuleA has been included into this module

  class ClassB  # class is a scope gate, creates new scope
    def initialize  # def is a scope gate, creates new scope
      c = ModuleA::ClassA.new  # must fully qualify ClassA
    end
  end

  ClassB2 = Class.new do  # no scope gate
    define_method :initialize do # no scope gate
      c = ClassA.new  # this works, no need to fully qualify
    end
  end
end

b = ModuleB::ClassB.new
b2 = ModuleB::ClassB2.new

在阅读了book "Metaprogramming Ruby" 之后,我开始了解 Ruby 中的作用域。真的很有启发性。

编辑:回应下面也的评论。

类本质上是一个 Ruby 常量(请注意,它是一个名称大写的对象)。常量在范围内具有定义的查找算法。 The Ruby Programming Language O'Reilly 的书在第 7.9 节中对其进行了很好的解释。这个blog post也有简要说明。

定义在任何类或模块之外的顶级常量就像顶级方法:它们隐式定义在 Object.当一个顶级常量从一个类中被引用时,它在继承层次结构的搜索过程中被解析。如果在模块定义中引用了常量,它会在搜索模块的祖先之后对 Object 进行显式检查。

这就是为什么在顶层包含 ModuleB 会使 ModuleB 中的类在所有模块、类和方法中可见。

【讨论】:

  • 我向所有想成为热门 Ruby 开发者的人推荐 Metaprogramming Ruby。
  • 那么,为什么顶层的include ModuleB 有什么帮助呢?范围门不应该阻止这在ModuleBClassB 内部产生影响吗?
  • 顶级常量,定义在任何类或模块之外,就像顶级方法:它们隐式定义在 Object.因此,当从类中引用顶级常量时,它会在继承层次结构的搜索过程中得到解决。但是,如果在模块定义中引用了常量,则在搜索对象的祖先之后需要对 Object 进行显式检查
  • 另外,这是个好问题。我已经编辑了答案以解决它,因为我无法将其放入 cmets 部分。
【解决方案2】:

原因是(我认为)与绑定有关。我的线索是以下内容也行不通:

module ModuleB
 include ModuleA

 class ClassB
  def initialize
   c = ClassA.new
  end
 end

 ClassB.new
end

ClassAClassB 定义中没有任何意义,因为它不是 ClassB 中的常量 - 该模块仅包含在其父模块中。进行此更改应该使一切正常:

module ModuleB
  include ModuleA
  class ClassB
    def initialize
      c = ModuleA::ClassA.new
    end
  end
end

【讨论】:

    猜你喜欢
    • 2012-03-20
    • 2013-02-09
    • 2013-07-07
    • 1970-01-01
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    • 2017-06-18
    • 2010-10-12
    相关资源
    最近更新 更多