Ruby的dup和clone方法有什么区别?

Ruby的文档dup说:

一般来说, clonedup在后代类中可能有不同的语义。 虽然clone用于复制对象,包括其内部状态,但dup通常使用后代对象的类来创build新实例。

但是当我做一些testing时,我发现它们实际上是一样的:

 class Test attr_accessor :x end x = Test.new xx = 7 y = x.dup z = x.clone yx => 7 zx => 7 

那么这两种方法有什么区别呢?

子类可以重写这些方法来提供不同的语义。 在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实现往往是我解决这些问题的根源,因为它非常清晰,并且是一个相当兼容的Ruby实现。

处理ActiveRecord时也有一个显着的不同:

dup创build一个没有设置id的新对象,所以你可以通过点击.save来保存一个新的对象到数据库

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

clone创build一个新的对象具有相同的ID,所以对新对象的所有更改将覆盖原始logging,如果击中.save

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

一个区别是冻结对象。 冻结对象的clone也被冻结(而不冻结对象的clone )。

 class Test attr_accessor :x end x = Test.new xx = 7 x.freeze y = x.dup z = x.clone yx = 5 => 5 zx = 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! 

较新的文档包括一个很好的例子:

 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> 

两者几乎完全相同,但克隆比dup更重要。 在克隆中,对象的冻结状态也被复制。 在dup中,它总是被解冻。

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