2011-10-28 70 views
3

如何從單例方法訪問實例變量?從單例方法訪問實例變量

class Test 
    def initialize(a) 
    @a = a 
    end 

    def item 
    item = "hola" 
    def item.singl 
     [self, @a].join(" + ") 
    end 
    item 
    end 
end 

test = Test.new("cao") 
item = test.item 
item.singl 
#=> ... @a is nil 

回答

6

嘗試使用define_method。 Def讓你進入一個新的範圍。

class Test 
    def initialize(a) 
    @a = a 
    end 

    def item 
    item = "hola" 
    item.singleton_class.send(:define_method, :singl) do 
     [self, @a].join(" + ") 
    end 

    item 
    end 
end 

test = Test.new("cao") 
item = test.item 
item.singl #=> "hola + " 

在你的例子中,你仍然有一個問題,在一個字符串@a的單例類中還沒有定義。這主要是因爲在這種情況下self是字符串實例,而不是存在@a的測試實例。要解決這個問題,你可以將實例變量重新綁定到別的東西上,但這可能不是你想要的行爲。你也可以在你的新單例類中設置實例變量。

例如,

重新綁定變量

class Test 
    def initialize(a) 
    @a = a 
    end 

    def item 
    item = "hola" 
    new_self = self 
    item.singleton_class.send(:define_method, :singl) do 
     [self, new_self.instance_variable_get(:@a)].join(" + ") 
    end 

    item 
    end 
end 

test = Test.new("cao") 
item = test.item 
item.singl 

設置一個實例字符串變量

class Test 
    def initialize(a) 
    @a = a 
    end 

    def item 
    item = "hola" 
    item.singleton_class.send(:define_method, :singl) do 
     [self, @a].join(" + ") 
    end 

    item.singleton_class.send(:define_method, :set) do 
     @a = "cao" 
    end 

    item 
    end 
end 

test = Test.new("cao") 
item = test.item 
item.set 
item.singl 

其重要的要注意這兩種方法之間的差異。在第一種方法中,我們通過原始對象保留對原始實例變量的引用。在第二種方法中,我們創建一個新的實例變量,綁定在新的單例類下,包含原始測試的副本。

如果您使用的是非本地對象,那麼您可能會混合使用這兩種方法。編輯:作爲Benoit指出,在第二種方法「set」方法應該只是一個attr_accessor。實際上,你可以設置實例變量而不需要定義新的方法。通過item.instance_variable_set(:@, "cao")

+0

我無法理解你的最後一個例子,你正在對項目類中的「cao」字符串進行硬編碼,而不是獲取傳遞給Test初始值設定項的內容。你是否想將它定義爲屬性設置器? –

+0

我很懶。我用這個區別來說明這樣一個事實,即用第二種方法,兩個@ a不一樣。我認爲這個方法可以推斷出這個方法「應該」是一個屬性設置器。我會澄清。謝謝! – diedthreetimes

1

你想設置的Test類的實例變量和字符串實例檢索,這些都是不一樣的對象和不共享實例變量。您可以執行以下操作以在兩個實例之間傳遞它:

class Test 
    def initialize(a) 
    @a = a 
    end 

    def item 
    item = "hola" 
    item.singleton_class.send :attr_accessor, :a 
    # set the item's @a with the Test instance one 
    item.a = @a 
    def item.singl 
     [self, @a].join(" + ") 
    end 
    item 
    end 
end 

test = Test.new("cao") 
item = test.item 
puts item.singl 
+0

在你的例子中,你有一個小問題。新項目引用@a的副本,而不是@a本身。如果test.a發生變化,這是一個探針。例如,這會導致不正確的結果。 item.singl; test.a =「hi」; item.singl。當然,這真的只適用於字符串... – diedthreetimes

+0

感謝您的警告,但我不會觸及我的答案。你的已經比我好多了;-) –

1

您並不試圖訪問item的實例變量。 itemString對象,而@aTest對象test的實例變量。

兩者都是獨立的。訪問來自item@a的唯一方式是在item例如test(或@a)中提及。

class Test 
    attr_reader :a 
    def initialize(a)  
    @a = a 
    end  

    def item  
    item = "hola" 
    def item.singl  
     [self, @parent.a].join(" + ")  
    end 
    item.instance_variable_set(:@parent, self) 

    item  
    end 
end 

test = Test.new("cao") 
item = test.item 
item.singl