2010-02-27 179 views
17

时考虑下面的类设置属性值仍然是nil。这可能吗?红宝石初始化对象

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

+0

排除初始化方法(这是你的意思?)使事情变得困难。在创建新的'Test'对象时,'name'是否每次都得到相同的初始化?还是应该得到在开放代码中指定的值? – DigitalRoss

+2

的动机是做类似于C#的东西 - http://msdn.microsoft.com/en-us/library/bb384062.aspx –

+0

有什么你不喜欢与我提出的解决方案? –

回答

0

您正在指示的代码将参数传递给initialize函数。你肯定会必须要么使用initialize,或使用更无聊的语法:

test = Test.new 
test.name = 'Some test object' 
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" 
18

OK,

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

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}" 
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方法(实际上是单线)。

0

会需要继承测试(在这里与自己的方法和初始化所示)例如为:

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

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。

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 

如果您传递的最后一件事是一个Hash它会绕过initialize,并立即拨打制定者。如果你传递了其他的东西,它会调用这些参数初始化。

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并呼吁superObject#new获取默认行为。 通常我们会定义initialize而不是,除了满足您的问题中的特定请求外,不建议使用此方法。

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

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