【问题标题】:How do I set an attr_accessor for a dynamic instance variable?如何为动态实例变量设置 attr_accessor?
【发布时间】: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

【问题讨论】:

    标签: ruby instance-variables attr attr-accessor


    【解决方案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 的那个。我添加了一个额外的答案,它只为您添加实例变量的实例添加方法。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-28
          • 2013-05-15
          • 1970-01-01
          相关资源
          最近更新 更多