如何为动态实例变量设置 attr_accessor?

Posted

技术标签:

【中文标题】如何为动态实例变量设置 attr_accessor?【英文标题】:How do I set an attr_accessor for a dynamic instance variable? 【发布时间】:2011-06-25 06:02:01 【问题描述】:

我在我的类中动态创建了一个实例变量:

class Mine
  attr_accessor :some_var

  def intialize
    @some_var = true
  end

  def my_number num
    self.instance_variable_set "@my_#num", num
  end
end

我现在如何将@my_#num 设为 attr 值?

例如我希望能够做到这一点:

dude = Mine.new
dude.my_number 1
dude.my_1
=> 1

【问题讨论】:

【参考方案1】:

另一个解决方案,define_singleton_method

class Mine
  def my_number num
    define_singleton_method("num_#num")  num 
  end
end

所有这些解决方案的一个副作用是,如果你用不同的数字多次调用它,你最终会在你的对象上得到一堆方法:

dude = Mine.new
dude.my_number 1
dude.my_number 5
dude.my_1
=> 1
dude.my_5
=> 5

我们可以通过删除旧方法来解决这个问题:

class Mine
  def my_number num
    old_num = @num
    if @num
      # need to use `old_num` local variable
      # instance var scope is different inside `class_eval`
      singleton_class.class_eval  remove_method("num_#old_num") 
    end

    @num = num

    define_singleton_method("num_#num")  @num 
  end
end

【讨论】:

【参考方案2】:

这个答案不会污染类空间,例如..如果我这样做mine.my_number 4,那么Mine 的其他实例将无法获得my_4 方法.. 发生这种情况是因为我们使用了单例类对象而不是类。

class Mine
  def my_number num
    singleton_class.class_eval  attr_accessor "my_#num" 
    send("my_#num=", num)
  end
end

a = Mine.new
b = Mine.new
a.my_number 10 #=> 10
a.my_10 #=> 10
b.my_10 #=> NoMethodError

【讨论】:

使用singleton_class有什么原因吗?同样是单线,class_eval attr_accessor :"my_#num" 语法会更干净:) @HalilÖzgür 您只想为该实例定义该方法,这就是您使用singleton_class 的原因,如果您使用self.class,那么所有实例都会获得该方法而您不希望那样.你的语法是对的,我会做出改变 btw singleton_class 和 class << self 是一样的 我的意思是上面的代码在没有singleton_class的情况下产生相同的输出。 @HalilÖzgür class_eval 是类方法,而不是实例方法【参考方案3】:

较旧的线程,但我发现它很有用,谢谢。这是代码 Dorkus Prime 的答案,但也从哈希中的名称\值中获取实例变量

@cookies = browser.cookies.to_a

@cookies.each do |cookie|
self.class.__send__(:attr_accessor, "#cookie[:name]")
self.__send__("#cookie[:name]=",cookie[:value])
end

【讨论】:

【参考方案4】:

您可能想要使用 OpenStruct:

require "ostruct"

class Mine < OpenStruct
end

dude = Mine.new
dude.my_number = 1
dude.my_number # => 1

我不知道你为什么要 dude.my_1 返回 1 - 这不是把你已经拥有的东西还给你吗?

【讨论】:

我很好奇你如何继承 OpenStruct ActiveRecord::Base @Trip 你不能从多个类继承。【参考方案5】:

这里的两种方法存在一个问题...如果在一个实例中设置了一个实例变量,那么它的访问器将对所有实例都可用,因为您是在 self.class 而不是 self 上定义方法。

dude = Mine.new
dude.my_number 1
puts dude.my_1
dudette = Mine.new
dudette.my_1 = 2    # works, but probably shouldn't
dudette.my_number 2
dude.my_2 = 3       # works, but probably shouldn't

您可能想要做的只是修改具有实例变量的实例:

class Mine
  # ...
  def my_number num
    class << self
      attr_accessor "my_#num"
    end
    self.send("my_#num=", num)
  end
end

这样,实例变量只能获得创建它们的对象的访问器。我也没有打扰 instance_variable_set,因为如果您正在设置访问器,那么我认为重用它会更好。但这是一种风格的召唤。这里最重要的是调用class &lt;&lt; self 而不是self.class

【讨论】:

当我尝试这个时,我收到错误“未定义的局部变量或方法 `num' for #”。似乎 num 变量超出了这里的范围。 啊,你是对的!我得再考虑一下。与此同时,这不是正确的解决方案。 :// 对我也不起作用。如果它不起作用,请删除您的答案。【参考方案6】:

简单。您可以在 my_number 方法中动态定义属性读取器:

  def my_number num
     self.instance_variable_set "@my_#num", num
     self.class.class_eval do
        define_method("my_#num")  num 
     end
  end

看看是否适合你

【讨论】:

【参考方案7】:

这可以使用__send__ 来完成。这里:

class Mine
  attr_accessor :some_var

  def intialize
    @some_var = true
  end

  def my_number num
    self.class.__send__(:attr_accessor, "my_#num")
    self.__send__("my_#num=", num)
  end
end

dude = Mine.new
dude.my_number 1
puts dude.my_1

=> 1

【讨论】:

这将在所有实例上定义访问器,而不仅仅是您在其上调用my_number 的那个。我添加了一个额外的答案,它只为您添加实例变量的实例添加方法。

以上是关于如何为动态实例变量设置 attr_accessor?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 dev vs prod @ionic/app-scripts 设置动态环境变量?

如何为 SqlDataSource 动态绑定变量参数

如何为机器上的所有 Bash 实例建立“共享”变量? [复制]

如何为 c++ 的不同变量类型的结构元素创建动态数组?

如何为多个动态 div 设置多个 Cookie

如何为 Android 动态壁纸创建设置活动