如何检查变量是否真的响应:dup?
我想使用value.respond_to?(:dup) ? value.dup : value
value.respond_to?(:dup) ? value.dup : value
,以便检查我是否可以复制一个对象,但它在布尔值,nil或类似“基元”等类型的TypeError
上失败。
我最终得到了:
begin value = value.dup rescue #ignore, use the original if no dup-able (eg nil, true, etc) end
有没有更好的办法?
奖金:它为什么回应:dup
?
不是深层次的dup
,只是针对这个问题。
编辑 :思考:
-
obj.class.methods.include? :new
obj.class.methods.include? :new
的很好,但有点过于hackish我认为它有不好的表现 -
Marshal
看起来也有点矫枉过正 - 一线救援可能是最好的解决方案,但目前不可能进行类型特定的一线救援( IIUC matz就是这样! ),并且@JörgWMittag提到它的错误。
- 我个人认为在对象级别定义的
dup
是错误的。
所以,引用@Linuxios
没有更好的方法
没有更好的方法。 在Object上定义了dup
,这意味着任何想要不响应它的类都需要重载它以引发exception。 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
您可以通过这种方式将其写为一行:
value = value.dup rescue value
非常清楚。
定义一个dup
方法是标准的,该方法为无法复制的类型引发TypeError
。 因此任何对象都会“回应”它。 你真的必须打电话给它并检查一下开始救援结束。
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
我认为它响应dup
的原因是Classinheritance自Object,它有一个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; }
我想你唯一能做的就是在你的方法中检查这些特殊常量。
有一种更好的方法,但不确定检查错误消息:
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 (eg nil, true, etc) end