dup的Ruby文档说:
通常,clone和dup在后代类中可能具有不同的语义。虽然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
那么这两种方法有什么不同呢?
dup的Ruby文档说:
通常,clone和dup在后代类中可能具有不同的语义。虽然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
那么这两种方法有什么不同呢?
当前回答
子类可以覆盖这些方法以提供不同的语义。在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创建了一个没有设置id的新对象,所以你可以通过点击.save将一个新对象保存到数据库中
category2 = category.dup
#=> #<Category id: nil, name: "Favorites">
Clone创建了一个具有相同id的新对象,因此如果点击.save,对该新对象所做的所有更改将覆盖原始记录
category2 = category.clone
#=> #<Category id: 1, name: "Favorites">
子类可以覆盖这些方法以提供不同的语义。在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实现。
更新的文档包含了一个很好的例子:
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
一个不同之处在于冷冻物体。冻结对象的克隆也被冻结(而冻结对象的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!