2010-02-27 103 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

希望這一個非常具體和有趣的請求的創造性解決方案。