什么';这是Ruby和x27之间的区别;s dup和克隆方法?

什么';这是Ruby和x27之间的区别;s dup和克隆方法?,ruby,clone,dup,Ruby,Clone,Dup,他们说: 通常,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则不会冻

他们说:

通常,
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
则不会冻结)

另一个区别是单例方法。同样的故事,
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!

子类可以重写这些方法以提供不同的语义。在
对象
本身中,有两个关键区别

首先,
clone
复制singleton类,而
dup
不复制

o = Object.new
def o.foo
  42
end

o.dup.foo   # raises NoMethodError
o.clone.foo # returns 42
class Foo
  attr_accessor :bar
end
o = Foo.new
o.freeze

o.dup.bar = 10   # succeeds
o.clone.bar = 10 # raises RuntimeError
其次,
clone
保留冻结状态,而
dup
不保留

o = Object.new
def o.foo
  42
end

o.dup.foo   # raises NoMethodError
o.clone.foo # returns 42
class Foo
  attr_accessor :bar
end
o = Foo.new
o.freeze

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

通常是我回答这些问题的来源,因为它非常清晰,并且是一个相当兼容的Ruby实现。

在处理ActiveRecord时,也有一个显著的区别:

在不设置id的情况下创建新对象,因此您可以通过点击
.save
将新对象保存到数据库中

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

两者几乎相同,但克隆比dup多做一件事。在克隆中,对象的冻结状态也会被复制。在dup中,它总是被解冻的

 f = 'Frozen'.freeze
  => "Frozen"
 f.frozen?
  => true 
 f.clone.frozen?
  => true
 f.dup.frozen?
  => false 
其中包括一个很好的例子:

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>
Klass类
属性存取器:str
结束
模块Foo
def foo;'福';结束
结束
s1=Klass.new#=>#
s1.扩展(Foo)#=>#
s1.foo#=>“foo”
s2=s1.clone#=>#
s2.foo#=>“foo”
s3=s1.dup#=>#
s3.foo#=>NoMethodError:未定义的方法“foo”#

您可以使用clone在Ruby中进行基于原型的编程。Ruby的对象类定义了克隆方法和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"
在上面的示例中,当我们将一个新对象指定给橙色克隆的color实例方法时,它不再引用与apple相同的对象。因此,我们现在可以修改橙色的颜色方法,而不影响apple的颜色方法,但是如果我们从apple克隆另一个对象,那么新对象将在复制的实例变量中引用与apple相同的对象

dup还将生成它正在复制的对象的浅拷贝,如果您要对dup执行上面所示的相同演示,您将看到它的工作方式完全相同。但是克隆和dup之间有两个主要区别。首先,正如其他人提到的,克隆复制冻结状态,而dup不复制。这是什么意思?Ruby中的“冻结”一词是不可变的一个深奥术语,它本身就是计算机科学中的一个术语,意味着某些东西无法改变。因此,Ruby中的冻结对象不能以任何方式修改;事实上,它是不变的。如果试图修改冻结对象,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
苹果类 属性存取器:颜色 def初始化 @颜色=‘红色’ 结束 结束 苹果 苹果,冷冻的? =>错误 苹果,冷冻 苹果,冷冻的? =>正确 apple.color='crimson' 运行时错误:无法修改冻结的苹果 apple.color“red crimson”#我们无法修改对象的状态,但我们肯定可以修改它所引用的对象! 橙色=apple.dup 橘子,冷冻的? =>错误 orange2=apple.clone 橙子,冷冻的? =>正确 orange.color='orange' =>“orange”#我们可以修改orange对象,因为我们使用了dup,它没有复制冻结状态 橙色2.color='orange' RuntimeError:无法修改冻结的Apple#orange2引发异常,因为冻结状态是通过克隆复制的 其次,更有趣的是,克隆复制了singleton类(以及它的方法)!如果您希望使用Ruby进行基于原型的编程,这是非常有用的。首先,让我们展示一下单例方法确实是用clone复制的,然后我们可以将它应用到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 
类水果
属性存取器:源
def初始化
@产地=:植物
结束
结束
水果
=> # 
种子?
真的
结束
2.4.1:013>水果单体法
=>[:种子?]
苹果=水果
=> # 
苹果种子?
=>正确
如您所见,fruit对象实例的singleton类被复制到克隆中。因此,克隆对象可以访问singleton方法:seeded?。但dup的情况并非如此:

apple = fruit.dup
 => #<Fruit:0x007fdafe0c6558 @origin=:plant> 
apple.seeded?
=> NoMethodError: undefined method `seeded?'
apple=fruit.dup
=> # 
苹果种子?
=>NoMethodError:未定义的方法'seeded'
现在在基于原型的编程中,您没有扩展其他类然后创建类实例的类,这些类的方法派生自用作蓝图的父类。相反,您有一个基本对象,然后从对象创建一个新对象,并复制其方法和状态(当然,因为我们通过克隆进行浅层复制,实例变量引用的任何对象都将像在JavaScript原型中一样共享)。然后,您可以通过填写克隆方法的详细信息来填充或更改对象的状态。在下面的示例中,我们有一个基本水果对象。所有水果都有种子,所以我们创建了一个种子的方法编号。但是苹果只有一颗种子,所以我们创造了一个克隆,并填入了细节。现在,当我们克隆苹果时,我们不仅克隆了方法,还克隆了状态!记得
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 
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