哪些 Ruby 类支持 .clone?
Posted
技术标签:
【中文标题】哪些 Ruby 类支持 .clone?【英文标题】:Which Ruby classes support .clone? 【发布时间】:2010-11-27 23:05:35 【问题描述】:Ruby 在 Object 中定义了#clone
。
令我惊讶的是,某些类在调用它时会引发异常。
我发现 NilClass、TrueClass、FalseClass、Fixnum 有这种行为。
1) 是否存在不允许 #clone
的类的完整列表(至少是核心类)?
或者有没有办法检测特定类是否支持#clone
?
2) 42.clone
有什么问题?
【问题讨论】:
我真的很想知道你如何测试一个类是否可以自己克隆。似乎如果一个类不想让自己被克隆,那么它应该将它从 Object 继承的克隆方法设为私有,这样你就可以只在 public_methods 下测试它的存在。对我来说似乎是常识。 【参考方案1】:Rails 似乎使用“可复制的?()”方法扩展了您提到的类。
http://api.rubyonrails.org/files/activesupport/lib/active_support/core_ext/object/duplicable_rb.html
【讨论】:
duplicatable?() 也在 ActiveSupport gem 中定义【参考方案2】:我对YARV的源代码做了git grep "can't clone"
,得到了
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)
。但这超出了我的理解范围。
【讨论】:
【参考方案3】:我仍然不知道如何正确测试可克隆性,但这是使用错误捕获来测试可克隆性的一种非常笨拙、邪恶的方法:
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
【讨论】:
【参考方案4】:Fixnum 是一个特殊的类,由语言给予特殊处理。从你的程序启动的那一刻起,类可以表示的每个数字都有一个 Fixnum,并且它们被赋予了一个不占用任何额外空间的特殊表示——这样,基本的数学运算就不会分配和释放记忆像疯了似的。因此,42 不能超过一个。
对于其他人来说,他们都有一个共同点:他们都是单身人士。根据定义,单例类只有一个实例,因此尝试克隆它是错误的。
【讨论】:
“因此,42 不能超过一个。”。为什么需要?这是完美的。【参考方案5】:我认为没有正式的列表,至少除非你算上阅读源代码。原因 2) 不起作用是因为对 Fixnums 进行了优化。它们作为实际值在内部存储/传递(true、false 和 nil 也是如此),而不是作为指针。幼稚的解决方案是让42.clone
返回相同的42
,但不变的obj.clone.object_id != obj.object_id
将不再成立,42.clone
实际上不会被克隆。
【讨论】:
obj.clone.object_id != obj.object_id
为真和obj.clone.object_id == obj.object_id
并不总是为真是不同的。前者不成立,不代表后者不成立。【参考方案6】:
你不能克隆不可变的类。 IE。您只能拥有一个对象 42 的实例(作为 Fixnum),但可以拥有多个“42”的实例(因为字符串是可变的)。您也不能克隆符号,因为它们类似于不可变字符串。
您可以使用 object_id 方法在 IRB 中检查。 (重复调用后,symbols 和 fixnums 会给你相同的 object_id)
【讨论】:
Mutability 与它无关(实际上,您可以将状态添加到 Fixnum)。 Fixnum 的默认行为真的很奇怪,特别是考虑到它确实具有克隆方法 d.class.method_defined?(:clone) == true以上是关于哪些 Ruby 类支持 .clone?的主要内容,如果未能解决你的问题,请参考以下文章
在 Ruby 类上定义哪种方法来为其实例提供 dup / clone?
.NET 3.5 中的 CultureInfo 类支持哪些文化?