在Struct实例中创建动态属性

时间:2014-12-30 05:48:46

标签: ruby

是否可以在Struct实例中动态创建属性?

class Person < Struct.new(:name)
end

p = Person.new("Bilbo")
p[:surname] = "Jenkins" # does not work

3 个答案:

答案 0 :(得分:1)

您可以使用OpenStruct

require 'ostruct'

p = OpenStruct.new(name: "Bilbo")
p[:surname] = "Jenkins"

p.surname # => "Jenkins"

答案 1 :(得分:1)

您可以通过以下方式在Person课程中定义新方法:

Person.send(:define_method, :surname){@surname}
Person.send(:define_method, :surname=){|x|@surname=x}

我更喜欢define_method而不是instance_eval,因为我尽可能省略eval

答案 2 :(得分:0)

您可以继承Struct,在这种情况下,您将创建两个类,但更常见的是创建一个类:

Person = Struct.new(:name)

Person.instance_methods(false)
  #=> [:name, :name=]

p = Person.new("Bilbo")
  #=> #<struct Person name="Bilbo">

Does `p` have an instance variable `@name` whose value is `"Bilbo"`?

p.instance_variables
  #=> []

不,它没有。相反,它有&#34;成员&#34;:

p.members
  #=> [:name]

可以将name视为具有Struct提供的访问者的实例变量吗?

p.name
  #=> "Bilbo"
p.name = "cat"
p.name
  #=> "cat"

是的!这是因为Struct实例的成员存储在您不打算直接访问的数组中,只能通过访问者访问。

我们可以动态添加Struct个成员吗?我不知道答案,但不提供方法来轻松完成。相反,只需添加实例变量和(可选)访问器。

我们可以添加一个实例变量并将其值设置为:

p.instance_variable_set('@surname', 'Jenkins')
  #=> "Jenkins"

p.instance_variables
  #=> [:@surname]

并使用以下命令检索其值:

p.instance_variable_get('@surname')
  #=> "Jenkins"

如果您希望为该变量创建访问器,这是一种方式:

p.class.instance_eval do
  attr_accessor :surname
end

p.surname
  #=> "Jenkins"
p.surname = 'cat'
  #=> "cat"
p.surname
  #=> "cat"

p.class.instance_methods(false)
  #=> [:name, :name=, :surname, :surname=]