Ruby - 初始化对象时设置属性值

时间:2010-02-27 19:23:53

标签: ruby

鉴于以下课程:

class Test
  attr_accessor :name
end

当我创建对象时,我想执行以下操作:

t = Test.new {Name = 'Some Test Object'}

目前,名称仍为nil。这可能吗?

注意:我不想添加初始化程序。

8 个答案:

答案 0 :(得分:18)

确定,

我想出了一个解决方案。它使用初始化方法,但另一方面完全按照你想要的那样做。

class Test
  attr_accessor :name

  def initialize(init)
    init.each_pair do |key, val|
      instance_variable_set('@' + key.to_s, val)
    end
  end

  def display
    puts @name
  end

end

t = Test.new :name => 'hello'
t.display
快乐吗? :)


使用继承的替代解决方案。注意,使用此解决方案,您无需显式声明attr_accessor!

class CSharpStyle
  def initialize(init)
    init.each_pair do |key, val|
      instance_variable_set('@' + key.to_s, val)
      instance_eval "class << self; attr_accessor :#{key.to_s}; end"
    end
  end
end

class Test < CSharpStyle
  def initialize(arg1, arg2, *init)
    super(init.last)
  end
end

t = Test.new 'a val 1', 'a val 2', {:left => 'gauche', :right => 'droite'}
puts "#{t.left} <=> #{t.right}"

答案 1 :(得分:9)

正如其他人所说,最简单的方法是定义initialize方法。如果您不想这样做,可以让您的班级继承自Struct

class Test < Struct.new(:name)
end

现在:

>> t = Test.new("Some Test Object")
=> #<struct Test name="Some Test Object">
>> t.name
=> "Some Test Object"

答案 2 :(得分:7)

通常的方法是使用必要的操作来执行复杂对象初始化传递块。可以在对象的上下文中评估此块,因此您可以轻松访问所有实例变量,方法等。

继续你的例子

class Test
  attr_accessor :name

  def initialize(&block)
    instance_eval(&block)
  end 
end

然后

t = Test.new { @name = 'name' }

t = Test.new do
  self.name = 'name'
  # other initialization, if needed
end

请注意,这种方式不需要复杂地更改initialize方法(实际上是单行方式)。

答案 3 :(得分:1)

如前所述,执行此操作的明智方法是使用Struct或定义Test#initialize方法。这正是结构和构造函数的用途。使用与属性相对应的选项哈希是最接近的C#示例,它是一个看似普通的Ruby约定:

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

但是在你的例子中,你正在做一些看起来更像是使用=的变量赋值的东西,所以让我们尝试使用块而不是哈希。 (你也使用Name这将是Ruby中的常量,我们将改变它。)

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

很酷,现在让它真正起作用:

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" }
# => #<Test:0x007f90d38bacc0 @name="something">
t.name
# => "something"

我们创建了一个带有构造函数的类,该构造函数接受一个块参数,该参数在新实例化的对象中执行。

因为你说你想避免使用initialize,所以我改为覆盖new并调用super来获取Object#new的默认行为。 通常我们会定义initialize而不是,除了在您的问题中满足特定请求之外,不建议使用此方法。

当我们将一个块传递给BlockInit的子类时,我们可以做的不仅仅是设置变量......我们基本上只是将代码注入initialize方法(我们避免写入)。如果您还想要一个initialize方法来处理其他内容(正如您在评论中提到的那样),您可以将其添加到Test,甚至不必调用super(因为我们的更改不是在BlockInit#initialize,而不是BlockInit.new

希望这是一个非常具体和有趣的请求的创造性解决方案。

答案 4 :(得分:0)

您指示的代码是将参数传递到initialize函数。你肯定要使用initialize,或者使用更无聊的语法:

test = Test.new
test.name = 'Some test object'

答案 5 :(得分:0)

需要子类Test(这里用自己的方法和初始化器显示),例如:

class Test
  attr_accessor :name, :some_var

  def initialize some_var
    @some_var = some_var
  end

  def some_function
    "#{some_var} calculation by #{name}"
  end
end

class SubClassedTest < Test
  def initialize some_var, attrbs
    attrbs.each_pair do |k,v|
      instance_variable_set('@' + k.to_s, v)
    end
    super(some_var)
  end
end

tester = SubClassedTest.new "some", name: "james"
puts tester.some_function

输出:some calculation by james

答案 6 :(得分:0)

你可以这样做。

class Test
   def not_called_initialize(but_act_like_one)
        but_act_like_one.each_pair do |variable,value|
            instance_variable_set('@' + variable.to_s, value)
            class << self
                    self
            end.class_eval do
                    attr_accessor variable
            end
        end
   end
end

(t = Test.new).not_called_initialize :name => "Ashish", :age => 33
puts t.name #=> Ashish
puts t.age  #=> 33

一个优点是您甚至不必使用attr_accessor预先定义实例变量。您可以通过not_called_initialize方法传递所需的所有实例变量,并在定义getter和setter之外创建它们。

答案 7 :(得分:0)

如果您不想覆盖initialize,则必须向上移动链并覆盖new。这是一个例子:

class Foo
  attr_accessor :bar, :baz

  def self.new(*args, &block)
    allocate.tap do |instance|
      if args.last.is_a?(Hash)
        args.last.each_pair do |k,v|
          instance.send "#{k}=", v
        end
      else
        instance.send :initialize, *args
      end
    end
  end

  def initialize(*args)
    puts "initialize called with #{args}"
  end
end

如果您传入的最后一件事是哈希,它将绕过initialize并立即致电设置者。如果你传递了其他内容,则会使用这些参数调用initialize。