动态定义的类错误地共享数据 - 错误或编码错误?

Cho*_*ett 5 ruby metaprogramming

我一直在尝试建立一个系统,我可以生成一系列类似的Ruby类,用整数参数区分,然后将其保存到相关类的类变量中 - 类似于C++模板.

但是,引用(因此,创建)模板化类的新版本会覆盖先前版本中保存的参数,而我无法解决原因.

这是一个最小的例子

class Object
  def self.const_missing(name)
    if name =~ /^Templ(\d+)$/
      return make_templ $1.to_i
    else
      raise NameError.new("uninitialised constant #{name}")
    end
  end

private
  def make_templ(base)
    # Make sure we don't define twice
    if Object.const_defined? "Templ#{base}"
      return Object.const_get "Templ#{base}"
    else
      # Define a stub class
      Object.class_eval "class Templ#{base}; end"

      # Open the class and define the actual things we need.
      Object.const_get("Templ#{base}").class_exec(base) do |in_base|        
        @@base = in_base

        def initialize
          puts "Inited with base == #{@@base}"
        end
      end

      Object.const_get("Templ#{base}")
    end
  end
end

irb(main):002:0> Templ1.new
Inited with base == 1
=> #<Templ1:0x26c11c8>
irb(main):003:0> Templ2.new
Inited with base == 2
=> #<Templ2:0x20a8370>
irb(main):004:0> Templ1.new
Inited with base == 2
=> #<Templ1:0x261d908>
Run Code Online (Sandbox Code Playgroud)

我在Ruby中发现了一个错误(ruby 1.9.2p290(2011-07-09)[i386-mingw32]),还是我编写了错误的错误?

dbe*_*hur 1

因为您首先在类 Object 的上下文中进行语法引用@@base,所以它是 Object 的类变量,并且 object 的所有 TemplX 子类都引用超类的类变量。您可以更改代码以使用Module#class_variable_setclass_variable_get避免超类中的绑定。

您的代码还有一些其他问题:我注意到您没有创建 的make_templ类方法对等体self.const_missing,尽管它已成功分派,因为 Object 是 Class 的祖先。当存在其他方法时,最好避免所有形式的 eval(string)。如果你不处理 const_missing ,你不应该引发 NameError ,而是分派给 super ,因为其他人可能在链中并且想要做一些事情来解析常量。

class Object
  def self.const_missing(name)
    if name =~ /^Templ(\d+)$/
      return make_templ $1.to_i
    end
    super
  end

private
  def self.make_templ(base)
    klass_name = "Templ#{base}"
    unless const_defined? klass_name
      klass = Class.new(Object) do
        class_variable_set :@@base, base
        def initialize
          puts "Inited with base == #{self.class.class_variable_get(:@@base)}"
        end
      end
      const_set klass_name, klass    
    end

    const_get klass_name
  end
end
Run Code Online (Sandbox Code Playgroud)

类变量通过继承具有有趣且通常不受欢迎的信息混合属性。你遇到了其中一个陷阱。我不知道您还需要什么其他属性@@base,但看起来您可能会使用类实例变量获得更好的隔离和更少的令人惊讶的结果。更多解释:FowlerRailsTips