2010-03-08 18 views
0

問: 使用Ruby是簡單的添加自定義方法現有的班,但你如何添加自定義性能?這裏是什麼,我試圖做一個例子:紅寶石:添加自定義屬性內置類

myarray = Array.new(); 
myarray.concat([1,2,3]); 
myarray._meta_ = Hash.new();  # obviously, this wont work 
myarray._meta_['createdby'] = 'dreftymac'; 
myarray._meta_['lastupdate'] = '1993-12-12'; 

## desired result 
puts myarray._meta_['createdby']; #=> 'dreftymac' 
puts myarray.inspect()   #=> [1,2,3] 

的目標是構建類定義這樣一種方式,並不在本例中工作的東西,上面的預期將工作。

更新:(澄清的問題),這是離開了原來的問題的一個方面,它還是要加上「默認值」,將通常被建立在初始化類的方法的目標。

更新:(爲什麼要這樣做)通常,創建一個從Array繼承的自定義類(或者任何想要模擬的內置類)非常簡單。這個問題來自於一些「僅測試」的代碼,並不是試圖忽略這種普遍接受的方法。

+0

是否有一個原因,你不能簡單地創建一個繼承自Array並添加所需功能的類? – 2010-03-08 02:23:14

+0

這是執行此操作的首選方式,但此問題僅與某些實驗性代碼有關,僅用於測試目的......我將更新該問題。 – dreftymac 2010-03-08 02:47:28

+0

儘管您已經接受了我的回答,但我已經添加了有關如何爲您添加的屬性(根據您的問題更新)設置默認值的說明。 – 2010-03-08 03:44:12

回答

1

回想一下,在Ruby中,您不能訪問該實例之外的屬性(實例變量)。您只能訪問實例的公共方法。

可以使用attr_accessor爲你描述充當屬性的類創建一個方法:

irb(main):001:0> class Array 
irb(main):002:1> attr_accessor :_meta_ 
irb(main):003:1> end 
=> nil 
irb(main):004:0> 
irb(main):005:0* x = [1,2,3] 
=> [1, 2, 3] 
irb(main):006:0> x._meta_ = Hash.new 
=> {} 
irb(main):007:0> x._meta_[:key] = 'value' 
=> "value" 
irb(main):008:0> 

對於一個簡單的方法做了一個訪問默認初始化,我們需要基本reimplement attr_accessor ourselves

class Class 
    def attr_accessor_with_default accessor, default_value 
    define_method(accessor) do 
     name = "@#{accessor}" 
     instance_variable_set(name, default_value) unless instance_variable_defined?(name) 
     instance_variable_get(name) 
    end 

    define_method("#{accessor}=") do |val| 
     instance_variable_set("@#{accessor}", val) 
    end 
    end 
end 

class Array 
    attr_accessor_with_default :_meta_, {} 
end 

x = [1,2,3] 
x._meta_[:key] = 'value' 
p x._meta_ 

y = [4,5,6] 
y._meta_[:foo] = 'bar' 
p y._meta_ 

但是等等!輸出不正確:

{:key=>"value"} 
{:foo=>"bar", :key=>"value"} 

我們已經創建了一個圍繞文字散列默認值的閉包。

一個更好的辦法可能是簡單地使用塊:

class Class 
    def attr_accessor_with_default accessor, &default_value_block 
    define_method(accessor) do 
     name = "@#{accessor}" 
     instance_variable_set(name, default_value_block.call) unless instance_variable_defined?(name) 
     instance_variable_get(name) 
    end 

    define_method("#{accessor}=") do |val| 
     instance_variable_set("@#{accessor}", val) 
    end 
    end 
end 

class Array 
    attr_accessor_with_default :_meta_ do Hash.new end 
end 

x = [1,2,3] 
x._meta_[:key] = 'value' 
p x._meta_ 

y = [4,5,6] 
y._meta_[:foo] = 'bar' 
p y._meta_ 

現在的輸出是正確的,因爲Hash.new被稱爲每次默認值被檢索的時間,而不是每次都重複使用相同的文字哈希值。

{:key=>"value"} 
{:foo=>"bar"} 
+0

是的,那是有效的。謝謝回覆。在原始編輯中忽略了一件事:如何初始化通常在* initialize *方法中設置的值。這是似乎沒有爲我工作的方面。 – dreftymac 2010-03-08 03:03:00

2

不是屬性只是一個getter和setter?如果是這樣,不能你只是做:

class Array 
    # Define the setter 
    def _meta_=(value) 
    @_meta_ = value 
    end 

    # Define the getter 
    def _meta_ 
    @_meta_ 
    end 
end 

然後,你可以這樣做:

x = Array.new 
x._meta_ 
# => nil 

x._meta_ = {:name => 'Bob'} 

x._meta_ 
# => {:name => 'Bob'} 

這是否幫助?

+0

是的,那是有效的。謝謝回覆。在原始編輯中忽略了一件事:如何初始化通常在* initialize *方法中設置的值。這是似乎沒有爲我工作的方面。 – dreftymac 2010-03-08 03:04:36