如何检查变量是否真的响应:dup?

Her*_*ess 15 ruby

我想用来value.respond_to?(:dup) ? value.dup : value检查我是否可以复制一个对象,但它失败了,TypeError在booleans,nil或类似的"原语"上.

我最终得到了:

begin
  value = value.dup
rescue
  #ignore, use the original if no dup-able (e.g nil, true, etc)
end
Run Code Online (Sandbox Code Playgroud)

有没有更好的办法?

额外奖励:为什么会回应:dup

不深dup,仅针对这个问题.

编辑:思考:

  • obj.class.methods.include? :new 很好,但有点过于hackish我觉得它有糟糕的表现
  • Marshal 看起来也有点矫枉过正
  • 一线救援可能是最好的解决方案,但目前不可能进行类型特定的一线救援(IIUC matz就是这样!),并且正如@JörgWMittag提到它的错误.
  • 我个人认为dup在对象级别定义是错误的.

所以,引用@Linuxios

没有更好的方法

Gui*_*nal 6

您可以通过这种方式将其写为一行:

value = value.dup rescue value
Run Code Online (Sandbox Code Playgroud)

非常清楚.

定义一种为无法复制的类型dup引发的方法是标准的TypeError.因此任何对象都会"回应"它.你真的要打电话给它,并检查开始救援结束.

  • 你应该*从不*盲目地从`StandardError`救出`.您应该只处理最具体的异常,在本例中为"TypeError". (6认同)

Lin*_*ios 5

没有更好的方法.dup在Object上定义,这意味着任何想要不响应它的类都需要重载它以引发异常.NilClass,TrueClass,FalseClass,和Number是对象的所有子类.这意味着他们必须覆盖该方法以抛出错误.

解决这个问题的一种方法,如果你正在寻找一个深层复制品,就是使用通常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
Run Code Online (Sandbox Code Playgroud)

  • @CarySwoveland:是的,这是一个真正的深拷贝,除非`obj`是数字,bool或nil. (2认同)