How can one set property values when initializing an object in Ruby?

前端 未结 8 2134
清歌不尽
清歌不尽 2021-01-01 16:09

Given the following class:

class Test
  attr_accessor :name
end

When I create the object, I want to do the following:

t = Test         


        
8条回答
  •  不思量自难忘°
    2021-01-01 16:28

    As previously mentioned, the sensible way to do this is either with a Struct or by defining an Test#initialize method. This is exactly what structs and constructors are for. Using an options hash corresponding to attributes is the closest equivalent of your C# example, and it's a normal-looking Ruby convention:

    t = Test.new({:name => "something"})
    t = Test.new(name: "something") # json-style or kwargs
    

    But in your example you are doing something that looks more like variable assignment using = so let's try using a block instead of a hash. (You're also using Name which would be a constant in Ruby, we'll change that.)

    t = Test.new { @name = "something" }
    

    Cool, now let's make that actually work:

    class BlockInit
      def self.new(&block)
        super.tap { |obj| obj.instance_eval &block }
      end
    end
    
    class Test < BlockInit
      attr_accessor :name
    end
    
    t = Test.new { @name = "something" }
    # => #
    t.name
    # => "something"
    

    We've created a class with a constructor that accepts a block argument, which is executed within the newly-instantiated object.

    Because you said you wanted to avoid using initialize, I'm instead overriding new and calling super to get the default behavior from Object#new. Normally we would define initialize instead, this approach isn't recommended except in meeting the specific request in your question.

    When we pass a block into a subclass of BlockInit we can do more than just set variable... we're essentially just injecting code into the initialize method (which we're avoiding writing). If you also wanted an initialize method that does other stuff (as you mentioned in comments) you could add it to Test and not even have to call super (since our changes aren't in BlockInit#initialize, rather BlockInit.new)

    Hope that's a creative solution to a very specific and intriguing request.

提交回复
热议问题