Adding an instance variable to a class in Ruby

前端 未结 8 649
太阳男子
太阳男子 2021-01-31 02:39

How can I add an instance variable to a defined class at runtime, and later get and set its value from outside of the class?

I\'m looking for a metaprogramming so

8条回答
  •  没有蜡笔的小新
    2021-01-31 03:37

    @Readonly

    If your usage of "class MyObject" is a usage of an open class, then please note you are redefining the initialize method.

    In Ruby, there is no such thing as overloading... only overriding, or redefinition... in other words there can only be 1 instance of any given method, so if you redefine it, it is redefined... and the initialize method is no different (even though it is what the new method of Class objects use).

    Thus, never redefine an existing method without aliasing it first... at least if you want access to the original definition. And redefining the initialize method of an unknown class may be quite risky.

    At any rate, I think I have a much simpler solution for you, which uses the actual metaclass to define singleton methods:

    m = MyObject.new
    metaclass = class << m; self; end
    metaclass.send :attr_accessor, :first, :second
    m.first = "first"
    m.second = "second"
    puts m.first, m.second
    

    You can use both the metaclass and open classes to get even trickier and do something like:

    class MyObject
      def metaclass
        class << self
          self
        end
      end
    
      def define_attributes(hash)
        hash.each_pair { |key, value|
          metaclass.send :attr_accessor, key
          send "#{key}=".to_sym, value
        }
      end
    end
    
    m = MyObject.new
    m.define_attributes({ :first => "first", :second => "second" })
    

    The above is basically exposing the metaclass via the "metaclass" method, then using it in define_attributes to dynamically define a bunch of attributes with attr_accessor, and then invoking the attribute setter afterwards with the associated value in the hash.

    With Ruby you can get creative and do the same thing many different ways ;-)


    FYI, in case you didn't know, using the metaclass as I have done means you are only acting on the given instance of the object. Thus, invoking define_attributes will only define those attributes for that particular instance.

    Example:

    m1 = MyObject.new
    m2 = MyObject.new
    m1.define_attributes({:a => 123, :b => 321})
    m2.define_attributes({:c => "abc", :d => "zxy"})
    puts m1.a, m1.b, m2.c, m2.d # this will work
    m1.c = 5 # this will fail because c= is not defined on m1!
    m2.a = 5 # this will fail because a= is not defined on m2!
    

提交回复
热议问题