Ruby的dup和clone方法有什么区别?
Ruby的文档dup
说:
一般来说,
clone
和dup
在后代类中可能有不同的语义。 虽然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