什么是Ruby的DUP和克隆方法之间的区别?什么是Ruby的DUP和克隆方法之间的区别?(What&

2019-05-13 13:57发布

在Ruby的文档的dup说:

在一般情况下, 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

那么,这两种方法之间的差异?

Answer 1:

子类可以重写这些方法以提供不同的语义。 在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实现。



Answer 2:

当ActiveRecord的处理有一个显著差异太大:

dup创建不其ID的新对象被设置,这样你就可以通过点击一个新的对象保存到数据库.save

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

clone创建具有相同ID的新对象,因此新对象所做的所有更改会覆盖,如果击中原始记录.save

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


Answer 3:

一个不同之处是冻结对象。 的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!


Answer 4:

两者都几乎相同,但克隆确实比DUP一两件事。 在克隆对象的冻结状态也被复制。 在DUP,它会永远被解冻。

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


Answer 5:

在新的文档包含一个很好的例子:

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>


Answer 6:

你可以用克隆做在Ruby中基于原型的编程。 Ruby的Object类定义了clone方法和DUP方法。 这两个克隆和DUP生产它是复制对象的浅表副本; 也就是说,该对象的实例变量被复制但不它们所引用的对象。 我将演示一个例子:

class Apple
  attr_accessor :color
  def initialize
    @color = 'red'
  end
end

apple = Apple.new
apple.color
 => "red"
orange = apple.clone
orange.color 
 => "red"
orange.color << ' orange'
 => "red orange" 
apple.color
 => "red orange"

在上述例子中的通知,橙色克隆拷贝的状态(即,实例变量)苹果对象的,但其中苹果对象引用其它对象(如字符串对象的颜色),这些参考文献不被复制。 相反,苹果和橘子都引用同一个对象! 在我们的例子中,参考的是字符串对象“红色”。 当橙色使用append方法,<<,修改现有的字符串对象,它改变了字符串对象到“红色橙色”。 这实际上改变了apple.color过,因为它们都指向同一个String对象。

作为边注,赋值操作符,=,将分配一个新的对象,从而摧毁的参考。 这里是一个演示:

class Apple
  attr_accessor :color
  def initialize
    @color = 'red'
  end
end

apple = Apple.new
apple.color
=> "red"
orange = apple.clone
orange.color
=> "red"
orange.color = 'orange'
orange.color
=> 'orange'
apple.color
=> 'red'

在上面的例子中,当我们分配了一个全新的对象到橙色克隆的颜色实例方法,它不再指向同一对象的苹果。 因此,我们现在可以修改橙的颜色的方法,而不会影响苹果的色法,但如果我们从苹果克隆另一个对象,这个新对象将复制的实例变量作为苹果引用同一个对象。

DUP也会产生被复制对象的浅拷贝,如果你是做给上面所示DUP同一演示中,你会看到它的工作原理完全一样。 但也有克隆和DUP之间的两个主要区别。 首先,正如其他人所提到的,克隆拷贝冻结状态和DUP无法解决。 这是什么意思? 在Ruby中“冻结”期限为不可变的一个深奥的术语,它本身是在计算机科学术语,意思是事情无法改变。 因此,在一个红宝石冻结对象不能以任何方式修改; 它实际上是不可变的。 如果你尝试修改冻结的对象,红宝石将提高抛出一个RuntimeError例外。 由于克隆复制冻结状态,如果你试图修改一个克隆的对象,这将提高抛出一个RuntimeError例外。 相反,由于DUP不会复制冻结状态,不会发生这样的例外,我们将演示:

class Apple
  attr_accessor :color
  def initialize
    @color = 'red'
  end
end

apple = Apple.new
apple.frozen?
 => false 
apple.freeze
apple.frozen?
 => true 
apple.color = 'crimson'
RuntimeError: can't modify frozen Apple
apple.color << ' crimson' 
 => "red crimson" # we cannot modify the state of the object, but we can certainly modify objects it is referencing!
orange = apple.dup
orange.frozen?
 => false 
orange2 = apple.clone
orange2.frozen?
 => true 
orange.color = 'orange'
 => "orange" # we can modify the orange object since we used dup, which did not copy the frozen state
orange2.color = 'orange'
RuntimeError: can't modify frozen Apple # orange2 raises an exception since the frozen state was copied via clone

其次,更有趣的是,克隆拷贝的单例类(因此它的方法)! 如果你渴望在Ruby中进行基于原型的编程这是非常有用的。 首先,让我们表明,确实是单身方法与克隆复制,然后我们就可以在基于Ruby的原型编程示例应用它。

class Fruit
  attr_accessor :origin
  def initialize
    @origin = :plant
  end
end

fruit = Fruit.new
 => #<Fruit:0x007fc9e2a49260 @origin=:plant> 
def fruit.seeded?
  true
end
2.4.1 :013 > fruit.singleton_methods
 => [:seeded?] 
apple = fruit.clone
 => #<Fruit:0x007fc9e2a19a10 @origin=:plant> 
apple.seeded?
 => true 

正如你所看到的,单身类水果的对象实例被复制到克隆。 因此克隆对象有权访问单方法:接种的? 但是,这不是与DUP的情况:

apple = fruit.dup
 => #<Fruit:0x007fdafe0c6558 @origin=:plant> 
apple.seeded?
=> NoMethodError: undefined method `seeded?'

现在,在基于原型的编程,你没有这扩展其他类的类,然后创建的类,它的方法从作为蓝本父类派生的实例。 取而代之的是,你有一个基本对象,然后创建从它的方法和状态复制(当然,因为我们是通过克隆做浅复制对象的新对象,实例变量引用任何对象将如同在JavaScript共享原型)。 然后,您可以填写或通过的克隆方法的细节填充改变对象的状态。 在下面的例子中,我们有一个基础水果对象。 所有的水果都有籽,所以我们创建了一个方法number_of_seeds。 但是苹果有一个种子,所以我们创建了一个克隆,并填写详细信息。 现在,当我们克隆苹果,我们不仅克隆方法,但是我们克隆了状态! 记住克隆确实状态(实例变量)的浅表副本。 正因为如此,当我们克隆苹果获得red_apple,red_apple将自动获得1种! 你可以认为red_apple的是,从苹果公司,这反过来从水果继承继承的对象。 因此,这就是为什么我资本水果和苹果。 我们废除了阶级之间的区别和对象克隆的礼貌。

Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
  @number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
  @number_of_seeds
end
 Apple = Fruit.clone
 => #<Object:0x007fb1d78165d8> 
Apple.number_of_seeds = 1
Apple.number_of_seeds
=> 1
red_apple = Apple.clone
 => #<Object:0x007fb1d892ac20 @number_of_seeds=1> 
red_apple.number_of_seeds
 => 1 

当然,我们可以在基于protoype编程构造方法:

Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
  @number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
  @number_of_seeds
end
def Fruit.init(number_of_seeds)
  fruit_clone = clone
  fruit_clone.number_of_seeds = number_of_seeds
  fruit_clone
end
Apple = Fruit.init(1)
 => #<Object:0x007fcd2a137f78 @number_of_seeds=1> 
red_apple = Apple.clone
 => #<Object:0x007fcd2a1271c8 @number_of_seeds=1> 
red_apple.number_of_seeds
 => 1 

最后,使用克隆,你可以得到类似的JavaScript原型行为的东西。



文章来源: What's the difference between Ruby's dup and clone methods?
标签: ruby clone dup