假設我有一個類:紅寶石添加方法一類
class Foo
end
要將方法添加到這個類,我知道2種選擇:
重新開放類並實現方法:
class Foo def bar end end
使用
class_eval
實現方法:Foo.class_eval { def bar; end}
有什麼區別?哪一個更好?
假設我有一個類:紅寶石添加方法一類
class Foo
end
要將方法添加到這個類,我知道2種選擇:
重新開放類並實現方法:
class Foo
def bar
end
end
使用class_eval
實現方法:
Foo.class_eval { def bar; end}
有什麼區別?哪一個更好?
實際上,還有其他一些方法可以將新方法添加到類中。例如,您也可以在模塊中定義方法,並將模塊混合到原始類中。
module ExtraMethods
def bar
end
end
Foo.class_eval { include ExtraMethods }
class Foo
include ExtraMethods
end
沒有真正的更好或更壞。您提到的兩種(或三種)方式有不同的行爲,您可能需要根據您的需要(或偏好)使用其中一種或另一種。在大多數情況下,這是主觀的。在其他情況下,它確實取決於代碼的結構。
重新打開類與使用class_eval
的主要目的區別在於第一個類也是類定義,而第二個類需要原始類已經定義。
實際上,在某些情況下重新開課可能會導致一些意想不到的副作用。假設您使用一堆方法在文件lib/foo.rb
中定義了Foo
。然後在config/initializers/extra.rb
中重新打開Foo
,並添加bar
方法。
在myclass.rb
中,您使用Foo
,但不需要手動輸入lib/foo.rb
,而是依靠自動加載功能。
如果extra.rb
被lib/foo.rb
之前加載,有什麼事情發生的是,Foo
類是在您的環境中已經定義,你的代碼將不會加載lib/foo.rb
。你將得到的是一個Foo
類,只包含你定義的bar
擴展名,而不包含原來的Foo
。
換句話說,如果無論出於何種原因重新開放類以添加某些方法而未確保首先(或之後)加載完整的原始類定義,則如果代碼依賴於自動加載,則代碼可能會中斷。
相反,Foo.class_eval
調用Foo
上的方法,因此它預計原始Foo
定義在嘗試添加新方法時已經存在。這可確保在添加新方法時,Foo
類將已定義。
總而言之,主要區別在於重新打開類允許您(無論好壞)將方法添加到尚未加載的類中,而class_eval
要求已經定義類。一般來說,除非我定義名稱空間子類或重新打開類,否則我完全控制它,我更喜歡第二種方法,因爲它在大型代碼庫中保持代碼更易維護。事實上,如果我擴展第三方類,我通常會使用mixins,以便我可以保留完整的方法祖先鏈,如果我需要覆蓋現有的方法。
沒有「Ruby自動加載」,你可能意思是「Rails autoload」。 – mudasobwa
@mudasobwa在技術上有('autoload'方法),但它不適用於這種情況。我對答案做了一些小改動。感謝您指出。 –
第二種方法非常方便,當你需要一些動態的東西。 Ruby實際上有幾個示波器:
# scope one, opened with `class` keyword
class ...
# scope two, opened with `def` keyword
def ...
end
end
使用class_eval
,您可以共享示波器。
>> foo = 1
=> 1
>> class Foo
>> puts foo
>> def bar
>> puts foo
>> end
>> end
NameError: undefined local variable or method 'foo' for Foo:Class
from (irb):3:in <class:Foo>
from (irb):2
>> Foo
=> Foo
>> Foo.class_eval {
?> puts foo
>> define_method :bar do
>> puts foo
>> end
>> }
1
=> :bar
>> Foo.new.bar
1
但是這也意味着這個共享範圍不會在類存在時被垃圾回收 – Vasfed
['Foo#define_method'](http://ruby-doc.org/core-2.2.0/Module.html#method-i-define_method)也是。 – mudasobwa