2012-04-17 76 views
186

Ruby docs for dup說:Ruby的dup和clone方法有什麼區別?

一般來說,clonedup可能在派生類不同的語義。雖然clone用於複製對象,包括其內部狀態,但dup通常使用後代對象的類來創建新實例。

但是,當我做了一些測試,我發現他們實際上是相同的:

class Test 
    attr_accessor :x 
end 

x = Test.new 
x.x = 7 
y = x.dup 
z = x.clone 
y.x => 7 
z.x => 7 

那麼什麼是兩種方法之間的差異?

+26

我希望我知道不是單純的區別*什麼*'dup'和'clone'做,但爲什麼* *你會使用一個而不是其他。 – 2012-04-17 05:22:34

+1

這裏也是一個很好的鏈接 - https://coderwall.com/p/1zflyg – 2013-09-22 07:08:34

回答

262

子類可以重寫這些方法來提供不同的語義。在Object本身,有兩個關鍵的區別。

首先,clone複製單例類,而dup沒有。

o = Object.new 
def o.foo 
    42 
end 

o.dup.foo # raises NoMethodError 
o.clone.foo # returns 42 

其次,clone保持凍結狀態,而dup沒有。

class Foo 
    attr_accessor :bar 
end 
o = Foo.new 
o.freeze 

o.dup.bar = 10 # succeeds 
o.clone.bar = 10 # raises RuntimeError 

Rubinius implementation for these methods 常常是我這些問題的答案來源,因爲這是很清楚的,並且一個相當標準的Ruby實現。

+13

如果有人試圖再次改變這種情況:「singleton class」是Ruby中一個明確定義的術語,它不僅包含singleton *方法*,還包含在singleton類中定義的所有常量。考慮:o = Object.new; class << o; A = 5;結束; puts(class << o.clone; A; end);放入(class << o.dup; A; end)'。 – 2013-04-24 17:14:54

+1

很好的答案,然後是一個很好的評論,但它導致我瘋狂追逐以理解語法。這將幫助那些可能也會感到困惑的人:http://www.devalot.com/articles/2008/09/ruby-singleton – davidpm4 2016-03-15 06:24:07

+1

我認爲值得一提的是,「單身人士課程」還包括任何模塊在原始物體上「延長」。所以'Object.new.extend(Enumerable).dup.is_a?(Enumerable)'返回false。 – Daniel 2016-09-01 01:21:32

28

一個區別是凍結對象。凍結對象的clone也被凍結(而凍結對象的dup不是)。

class Test 
    attr_accessor :x 
end 
x = Test.new 
x.x = 7 
x.freeze 
y = x.dup 
z = x.clone 
y.x = 5 => 5 
z.x = 5 => TypeError: can't modify frozen object 

另一個區別是單身方法。同樣的故事在這裏,dup不會複製那些,但clone呢。

def x.cool_method 
    puts "Goodbye Space!" 
end 
y = x.dup 
z = x.clone 
y.cool_method => NoMethodError: undefined method `cool_method' 
z.cool_method => Goodbye Space! 
+0

這對我來說非常有用。如果你正在創建一個凍結的常量值並將其傳遞給如下所示:https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L248(Rails cookie處理),那麼你可以當他們不知道你會輕易得到一個錯誤,他們克隆它,然後嘗試修改克隆。重複你的凍結價值,並通過它讓你至少保證沒有人意外地修改你的常數,而不會在這裏打破機架。 – XP84 2016-07-14 23:07:43

151

當ActiveRecord的處理有一個顯著差異太大:

dup創建不其ID的新對象被設置,這樣你就可以通過點擊.save

category2 = category.dup 
#=> #<Category id: nil, name: "Favorites"> 

clone保存新對象到數據庫創建一個具有相同ID的新對象,因此對該新對象所做的所有更改將覆蓋原始記錄(如果打上.save

category2 = category.clone 
#=> #<Category id: 1, name: "Favorites"> 
+35

這個答案是有IMO最重要的實用信息之一...其他答案在隱祕的地方,而這個答案指出了一個關鍵的實際區別。 – jpwynn 2015-03-12 06:53:58

+26

雖然上面是特定於ActiveRecord的;標準Ruby中的區別更加微妙。 – ahmacleod 2015-08-02 19:14:34

+1

@Stefan和@jvalanen:當我在'ActiveRecord'對象上應用'dup'和'clone'方法時,我得到了你在答案中提到的結果。這意味着當我使用'dup'時,它創建一個新的對象並設置它的'id',並在使用'clone'時創建一個沒有設置'id'的對象。你可以再看看它並清理嗎? 。 Thnx – 2016-08-03 10:39:38

2

兩者幾乎都是相同的,但克隆比dup更多。在克隆中,對象的凍結狀態也被複制。在dup中,它會一直解凍。

f = 'Frozen'.freeze 
    => "Frozen" 
f.frozen? 
    => true 
f.clone.frozen? 
    => true 
f.dup.frozen? 
    => false 
3

newer doc包括一個很好的例子:

class Klass 
    attr_accessor :str 
end 

module Foo 
    def foo; 'foo'; end 
end 

s1 = Klass.new #=> #<Klass:0x401b3a38> 
s1.extend(Foo) #=> #<Klass:0x401b3a38> 
s1.foo #=> "foo" 

s2 = s1.clone #=> #<Klass:0x401b3a38> 
s2.foo #=> "foo" 

s3 = s1.dup #=> #<Klass:0x401b3a38> 
s3.foo #=> NoMethodError: undefined method `foo' for #<Klass:0x401b3a38> 
相關問題