哪些Ruby类支持.clone?

时间:2009-09-13 18:59:22

标签: ruby clone

Ruby在对象中定义#clone。 令我惊讶的是,有些课程在调用时会引发异常。 我发现 NilClass TrueClass FalseClass Fixnum 有此行为。

1)是否存在完整的类列表(至少是核心类),不允许#clone? 或者有没有办法检测特定类是否支持#clone

2)42.clone有什么问题?

6 个答案:

答案 0 :(得分:7)

我认为没有正式的清单,至少除非你算上读源。原因2)不起作用是因为应用于Fixnums的优化。它们作为实际值在内部存储/传递(因此是true,false和nil),而不是作为指针。天真的解决方案是让42.clone返回相同的42,但是不变的obj.clone.object_id != obj.object_id将不再成立,42.clone实际上不会克隆。

答案 1 :(得分:5)

Fixnum是一个特殊的课程,由语言进行特殊处理。从程序启动时起,对于类可以表示的每个数字,只有一个Fixnum,并且它们被赋予一个不占用任何额外空间的特殊表示 - 这样,基本的数学运算就不会分配和解除分配记忆犹如疯狂。因此,不能超过一个42。

对于其他人来说,他们都有一个共同点:他们是单身人士。根据定义,只有一个单例类的实例,因此尝试克隆它是一个错误。

答案 2 :(得分:1)

我仍然不知道如何正确地测试克隆性,但这是使用错误捕获来测试clonablity的一种非常笨重,邪恶的方法:

def clonable?(value)
  begin
    clone = value.clone
    true
  rescue
    false
  end
end

这就是你如何克隆甚至不可克隆的东西。至少对于很少的课程,我已经厌倦了。

def super_mega_clone(value)
  eval(value.inspect)
end

以下是一些示例测试:

b = :b
puts "clonable? #{clonable? b}"

b = proc { b == "b" }
puts "clonable? #{clonable? b}"

b = [:a, :b, :c]
c = super_mega_clone(b)

puts "c: #{c.object_id}"
puts "b: #{b.object_id}"
puts "b == c => #{b == c}"
b.each_with_index do |value, index|
  puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}"
end
b[0] = :z

puts "b == c => #{b == c}"
b.each_with_index do |value, index|
  puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}"
end

b = :a
c = super_mega_clone(b)
puts "b: #{b.object_id} c: #{c.object_id}"

> clonable? false
> clonable? true
> c: 2153757040
> b: 2153757480
> b == c => true
> [0] b: 255528 c: 255528
> [1] b: 255688 c: 255688
> [2] b: 374568 c: 374568
> b == c => false
> [0] b: 1023528 c: 255528
> [1] b: 255688 c: 255688
> [2] b: 374568 c: 374568
> b: 255528 c: 255528

答案 3 :(得分:1)

我做了git grep "can't clone" YARV的源代码,得到了

lib/singleton.rb:    raise TypeError, "can't clone instance of singleton #{self.class}"
object.c:        rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj));
test/test_singleton.rb:    expected = "can't clone instance of singleton TestSingleton::SingletonTest"

第一行和第三行表示您无法克隆单例。

第二行是rb_special_const_p(obj)。但这超出了我的范围。

答案 4 :(得分:0)

您无法克隆不可变类。即你只能有一个对象42的实例(作为Fixnum),但可以有很多“42”的实例(因为字符串是可变的)。你也不能克隆符号,因为它们就像不可变的字符串。

您可以使用object_id方法在IRB中检查它。 (符号和fixnums在重复调用后会给你相同的object_id)

答案 5 :(得分:0)

Rails似乎使用“可复制的?()”方法扩展了您提到的类。

http://api.rubyonrails.org/files/activesupport/lib/active_support/core_ext/object/duplicable_rb.html