保证在Ruby中按身份比较对象的方法

时间:2011-10-25 04:20:01

标签: ruby compare

在Ruby中通过标识比较两个对象的保证方法是什么?给定两个变量,如果变量指向内存中完全相同的对象,我想返回true。

对于大多数Ruby对象,equal?方法按身份进行比较:

f = g = Object.new
p f.equal? g  # => true

但是,这不适用于所有对象。例如:

class F
  def ==(obj) false end
  def ===(obj) false end
  def eql?(obj) false end
  def equal?(obj) false end
  def object_id; self end
end

f = g = F.new
p f == g       # => false
p f === g      # => false
p f.eql? g     # => false
p f.equal? g   # => false
p f.object_id == g.object_id  # => false

通过身份比较两个对象是一种万无一失的保证方法,这种方法无法被击败?

这是一个纯粹的智力问题。任何以“为什么”开头的问题的答案都可能是“因为我很好奇。”

3 个答案:

答案 0 :(得分:13)

您可以获取Object#object_id的未绑定版本,将其绑定到相关对象,然后查看其内容。给你的F课程增加一个:

class F
  # ...
  def inspect; 'pancakes!' end # Just so we can tell what we have later.
end

然后:

>> f = F.new
>> f.object_id
=> pancakes!
>> unbound_object_id = Object.instance_method(:object_id)
>> unbound_object_id.bind(f).call
=> 2153000340
>> ObjectSpace._id2ref(2153000340).inspect
=> "pancakes!"

当然,如果有人打开对象并替换object_id,那么你运气不好,但如果有人这样做,这将是你遇到的最少的问题。如果您可以在加载任何其他内容之前抓住unbound_object_id UnboundMethod,那么如果有人更改Object#object_id,则unbound_object_id仍然是原始的正确版本将无关紧要。

因此,这个围绕黑客攻击会为任何对象提供可靠的object_id(受上述警告影响)。现在,您可以抓取并比较对象ID以获得可靠的比较。

答案 1 :(得分:6)

使用BasicObject#equal?。根据{{​​3}}:

  

与==不同,相等?方法永远不应该被子类覆盖:它用于确定对象标识(即a.equal?(b)iff a是与b相同的对象。)

需要更强的保证吗? AFAIK无法接收,BaseObject#object_idObjectSpace._id2refHash#compare_by_identity也可以覆盖或修补猴子(至少这是我个人的看法)。

答案 2 :(得分:3)

mu的答案太短很好,但是还有另一种方法可以使用Hash类的特殊功能来实现:

def compare_by_identity(x, y)
  h = {}.compare_by_identity
  h[x] = 1
  h[y] = 2
  h.keys.size == 1
end

在Ruby 1.9.2中添加了compare_by_identity功能,因此该功能在早期版本中不起作用。我认为 mu太短的答案更好。