为什么我不能在初始化中使用attr_accessor?

Geo*_*Geo 13 ruby eval metaprogramming

我正在尝试做一个instance_eval跟随attr_accessor内部initialize,我继续得到这个:``initialize':未定义的方法'attr_accessor'.为什么这不起作用?

代码看起来像这样:

class MyClass
   def initialize(*args)
      instance_eval "attr_accessor :#{sym}"
   end
end
Run Code Online (Sandbox Code Playgroud)

sep*_*p2k 20

您无法在实例上调用attr_accessor,因为attr_accessor未定义为MyClass的实例方法.它仅适用于模块和类.我怀疑你想在实例的元类上调用attr_accessor,如下所示:

class MyClass
  def initialize(varname)
    class <<self
      self
    end.class_eval do
      attr_accessor varname
    end
  end
end

o1 = MyClass.new(:foo)
o2 = MyClass.new(:bar)
o1.foo = "foo" # works
o2.bar = "bar" # works
o2.foo = "baz" # does not work
Run Code Online (Sandbox Code Playgroud)

  • 不,这不对.`class << self; ......; end`不是封闭.您将无法在其中访问`varname`,但您可以在`class_eval`块中访问它. (4认同)

Rob*_*ice 5

更清晰的实现(注意:这将为该类的所有实例添加访问器,而不仅仅是单个实例,请参阅下面的注释):

class MyClass
  def initialize(varname)
    self.class.send(:attr_accessor, varname)
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 这将访问器定义为`MyClass`的实例方法.因此,如果你有`o1 = MyClass.new(:foo)`和`o2 = MyClass.new(:bar)``o1`和`o2`都有`foo`和`bar`的访问者,这不是预期的行为. (6认同)

sem*_*ant 5

Rob d'Apice 几乎是对的.你只需要写:

self.singleton_class.send(:attr_accessor, varname)
Run Code Online (Sandbox Code Playgroud)

要么

self.singleton_class.class_eval "attr_accessor :#{varname}"
Run Code Online (Sandbox Code Playgroud)

或者我最喜欢的变种

self.singleton_class.class_exec do attr_accessor varname end
Run Code Online (Sandbox Code Playgroud)

假设值varname是一个符号