【问题标题】:adding attributes to a Ruby object dynamically动态地向 Ruby 对象添加属性
【发布时间】:2020-01-03 16:51:15
【问题描述】:

我创建了一个对象,我希望根据一些条件检查向该对象添加一些属性。我怎样才能做到这一点? 解释我想要什么:

A=Object.new
if(something happens)
{
  #make A have another attibute say age
  #& store something in A.age

}

【问题讨论】:

    标签: ruby


    【解决方案1】:

    首先,关于 ruby​​ 的一点是,它允许使用不同的语法,这种语法被 ruby​​ 编码人员广泛使用。大写的标识符是类或常量(感谢sepp2k 的评论),但您尝试将其设为对象。而且几乎没有人使用{} 来标记多行块。

    a = Object.new
    if (something happens)
      # do something
    end
    

    我不确定你的问题是什么,但我有一个想法,这就是解决方案:

    如果您知道该类可以具有哪些属性,并且它是有限的,您应该使用

    class YourClass
      attr_accessor :age
    end
    

    attr_accessor :age 的缩写:

    def age
      @age
    end
    def age=(new_age)
      @age = new_age
    end
    

    现在你可以这样做了:

    a = YourClass.new
    if (some condition)
      a.age = new_value
    end
    

    如果您想完全动态地执行此操作,可以执行以下操作:

    a = Object.new
    if (some condition)
      a.class.module_eval { attr_accessor :age}
      a.age = new_age_value
    end
    

    【讨论】:

    • 在类中使用大写字母不是 ruby​​ “语法”,而是通用约定。我已经告诉过你了——我还不是一个红宝石人。顺便说一句,您的解决方案对我来说已经足够好了。谢谢。
    • 我知道这是常见的约定,这就是为什么我写了“大写标识符应该是类”,我看到你是 ruby​​ 的新手,所以我想,我向您展示大多数 ruby​​ 编码人员是如何做到的。语法是关于 if 块的括号
    • @Nikhil:实际上它是 is 语法,不是约定俗成的问题。任何以大写字母开头的标识符都是 ruby​​ 中的常量。所以a = Object.newA = Object.new 做了太多不同的事情(第一个定义一个局部变量,另一个定义一个全局常量)。
    • 啊...这对我很有用!想知道我怎么从来没有遇到过。无论如何谢谢:)
    • 但这会使类的所有实例都具有此属性,而不仅仅是对象
    【解决方案2】:

    您可以使用 OpenStruct:

    a = OpenStruct.new
    if condition
        a.age = 4
    end
    

    或者只使用普通的旧哈希。

    【讨论】:

    • 太棒了!非常感谢!
    【解决方案3】:

    这只会将属性添加到对象:

    a = Object.new
    if (something happens)
      class << a
        attr_accessor :age
      end
      a.age = new_age_value
    end
    

    【讨论】:

      【解决方案4】:

      如果在编写代码时该属性的名称是已知的,那么您可以执行已经建议的操作:

      class A
      end
      
      a = A.new
      a.age = 20 # Raises undefined method `age='
      
      # Inject attribute accessors into instance of A
      class << a
        attr_accessor :age
      end
      
      a.age = 20 # Succeeds
      a.age # Returns 20
      
      b = A.new
      b.age # Raises undefined method, as expected, since we only modified one instance of A
      

      但是,如果属性的名称是动态的并且仅在运行时知道,那么您将不得不稍微不同地调用 attr_accessor

      attr_name = 'foo'
      
      class A
      end
      
      a = A.new
      a.foo = 20 # Raises undefined method `foo='
      
      # Inject attribute accessors into instance of A
      a.instance_eval { class << self; self end }.send(:attr_accessor, attr_name)
      
      a.foo = 20 # Succeeds
      a.foo # Returns 20
      

      【讨论】:

      • 你也可以a.singleton_class.send(:attr_accessor, attr_name)。谢谢!
      【解决方案5】:

      您可以使用instance_variable_set 在 Object 类的实例上设置变量(并使用instance_variable_get 获取它。)Object 类没有任何可以直接设置的属性。

      但是,我怀疑这不是您想要实现的最佳解决方案;直接使用 Object 类不是规范,而是定义或使用从它继承的具有您想要使用的属性的类。

      【讨论】:

        【解决方案6】:
        class Person; end
        person = Person.new
        person.age # throws NoMethodError
        
        if true
          person.singleton_class.module_eval { attr_accessor :age }
        end
        
        person.age = 123
        person.age #=> 123
        
        person2 = Person.new
        person2.age #=> throws NoMethodError
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-10-14
          • 2012-01-12
          • 1970-01-01
          • 2013-01-25
          • 1970-01-01
          • 2022-09-27
          • 2015-12-12
          • 2017-05-26
          相关资源
          最近更新 更多