代码之家  ›  专栏  ›  技术社区  ›  user1934428

块中未定义外部作用域的Ruby变量-为什么?

  •  3
  • user1934428  · 技术社区  · 6 年前

    这部分代码动态创建几个类:

    (1..MAX_ACT).each do |act_id|
      klass = Class.new(ActB) do
        def initialize(trg)
          super(trg, act_id)
        end
      end
      Object.const_set("Act#{act_id}", klass)
    end
    

    ActB )有一个带两个参数的构造函数,而子类有一个带一个参数的构造函数。

    运行这段代码很好,但是当我稍后尝试实例化其中一个类时,例如

    Act3.new(4)
    

    我收到错误信息

    NameError: undefined local variable or method `act_id' for #<Act3:0x00000006008b7990>
    

    super(trg, act_id)
    

    因为这是我计划中唯一的地方 使用

    (1..MAX_ACT).each do |act_id|
    

    我早就料到 做…结束 块为构造函数创建一个闭包,其中 行动id 已绑定。然而,情况似乎并非如此。

    3 回复  |  直到 6 年前
        1
  •  4
  •   matthewd    6 年前

    def (和 class module )创建一个新的本地范围,它不从外部继承任何本地范围。

    所以你说得对 Class.new do .. end 创建闭包。。。但是内心 定义 不会分享的。

    如果需要标准块行为,可以使用 define_method

    (1..MAX_ACT).each do |act_id|
      klass = Class.new(ActB) do
        define_method :initialize do |trg|
          super(trg, act_id)
        end
      end
      Object.const_set("Act#{act_id}", klass)
    end
    
        2
  •  1
  •   Aleksei Matiushkin    6 年前

    只是出于好奇,有一个黑客,允许愚弄范围和仍然使用 def initialize

    class ActB
      def initialize(trg, act_id)
        puts "ActID: #{act_id}"
      end 
    end
    (1..MAX_ACT).each do |act_id|
      klass = Class.new(ActB) do
        @act_id = act_id
        def initialize(trg)
          super(trg, self.class.instance_variable_get(:@act_id))
        end 
      end 
      Object.const_set("Act#{act_id}", klass)
    end
    
    Act1.new :foo
    #⇒ ActID: 1
    Act2.new :foo
    #⇒ ActID: 2
    
        3
  •  0
  •   thesecretmaster azazdeaz    6 年前

    这里的问题是 Class.new 在该类的上下文中执行。在那个班里, act_id 未定义。因此,要解决此问题,可以将方法定义移到类初始化之外,如下所示:

    (1..MAX_ACT).each do |act_id|
      klass = Class.new(ActB)
      klass.define_method(:initialize) do |trg|
        super(trg, act_id)
      end
      Object.const_set("Act#{act_id}", klass)
    end