我想使用value.respond_to?(:dup) ? value.dup : value
来检查我是否可以复制一个对象,但它在布尔值,零或类似“基元”等情况下以TypeError
失败。
我最终得到了:
begin
value = value.dup
rescue
#ignore, use the original if no dup-able (e.g nil, true, etc)
end
有更好的方法吗?
奖金:为什么它会回复:dup
?
不深dup
,仅针对此问题。
编辑:想法:
obj.class.methods.include? :new
很好,但有点过于苛刻,我觉得性能不好Marshal
看起来也有点矫枉过正dup
是错误的。所以,引用@Linuxios
没有更好的方法
答案 0 :(得分:6)
你可以这样写一行:
value = value.dup rescue value
很清楚。
定义dup
方法是标准的,可以为无法复制的类型引发TypeError
。因此任何对象都会“回应”它。你必须打电话给它并检查一下begin-rescue-end。
答案 1 :(得分:5)
没有更好的方法。 dup
在Object上定义,这意味着任何想要不响应它的类都需要重载它以引发异常。 NilClass
,TrueClass
,FalseClass
和Number
都是Object的子类。这意味着他们必须覆盖该方法以抛出错误。
解决这个问题的一种方法,如果你正在寻找一个深层拷贝,就是使用通常的Marshal.load(Marshal.dump(obj))
来处理数字,bool和nil就好了。
例如:
1.9.3-p392 :001 > obj = "hi"
=> "hi"
1.9.3-p392 :002 > Marshal.load(Marshal.dump(obj)).object_id != obj.object_id
=> true
1.9.3-p392 :003 > obj = 3
=> 3
1.9.3-p392 :004 > Marshal.load(Marshal.dump(obj)).object_id != obj.object_id
=> false
答案 2 :(得分:2)
def dupable?(obj)
obj.class.methods.include? :new
end
dupable?(1) # => false
dupable?(3.2) # => false
dupable?(:a) # => false
dupable?(true) # => false
dupable?(nil) # => false
dupable?("cat") # => true
答案 3 :(得分:1)
我认为它响应dup
的原因是Class继承自具有dup
方法的Object。
似乎在对象的dup
方法中检查'特殊常量'并引发您看到的错误:
VALUE
rb_obj_dup(VALUE obj)
{
VALUE dup;
if (rb_special_const_p(obj)) {
rb_raise(rb_eTypeError, "can't dup %s", rb_obj_classname(obj));
}
dup = rb_obj_alloc(rb_obj_class(obj));
init_copy(dup, obj);
rb_funcall(dup, id_init_dup, 1, obj);
return dup;
}
我想您唯一能做的就是在方法中检查这些特殊常量。
答案 4 :(得分:1)
有一种更好的方法,但不确定是否检查错误消息:
begin
value = value.dup
rescue TypeError => e
# !!! not sure about the following line
raise unless e.message == "can't dup %s" % value.class.name
#ignore, use the original if no dup-able (e.g nil, true, etc)
end