Ruby在对象中定义#clone
。
令我惊讶的是,有些课程在调用时会引发异常。
我发现 NilClass , TrueClass , FalseClass , Fixnum 有此行为。
1)是否存在完整的类列表(至少是核心类),不允许#clone
?
或者有没有办法检测特定类是否支持#clone
?
2)42.clone
有什么问题?
答案 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