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

发布于 2024-12-11 11:18:49 字数 654 浏览 0 评论 0原文

在 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

通过身份比较两个对象且无法被击败的万无一失/有保证的方法是什么?

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

What is a guaranteed way to compare two objects by their identity in Ruby? Given two variables, I want to return true if the variables point to the exact same object in memory.

For most Ruby objects, the equal? method compares by identity:

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

However, this doesn't work for all objects. For example:

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

What is a foolproof/guaranteed way of comparing two objects by identity which can't be defeated?

This is a purely intellectual question. The answer to any question that begins with "why" will probably be "Because I am curious."

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

勿忘心安 2024-12-18 11:18:49

您可以获取 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 并替换 object_id 那么您就不走运了,但这将是您的最少的如果有人这样做就会出现问题。如果您可以在执行其他操作之前获取您的 unbound_object_id UnboundMethod加载,那么即使有人更改 Object#object_id 也没关系,因为您的 unbound_object_id 仍然是原始的正确 ID。

因此,这个迂回的黑客行为为您提供了任何对象的可靠object_id(受上述警告的约束)。现在您可以抓取并比较对象 ID,以获得可靠的比较。

You could grab an unbound version of Object#object_id, bind it to the object in question, and see what it says. Given your F class with one addition:

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

Then:

>> 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!"

Of course, if someone opens up Object and replaces object_id then you're out of luck but this will be the least of your problems if someone does that. If you can grab your unbound_object_id UnboundMethod before anything else is loaded, then it won't matter if someone changes Object#object_id as your unbound_object_id will still be the original correct one.

So this round-about hack gives you a reliable object_id for any object (subject to the caveats above). Now you can grab and compare the object ids to get your reliable comparison.

南风几经秋 2024-12-18 11:18:49

使用BasicObject#equal?。根据 Ruby 文档

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

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

Use BasicObject#equal?. According to Ruby documentation:

Unlike ==, the equal? method should never be overridden by subclasses: it is used to determine object identity (that is, a.equal?(b) iff a is the same object as b).

Need stronger guarantee? AFAIK can't receive it, BaseObject#object_id, ObjectSpace._id2ref, Hash#compare_by_identity can be overriden or monkey patched too (at least this is my personal belief).

錯遇了你 2024-12-18 11:18:49

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 中添加了 compare_by_identity 功能.2,所以这个功能在早期版本中不起作用。我认为mu太短的答案更好。

The answer by mu is too short is great, but there is another way you can do it using a special feature of the Hash class:

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

The compare_by_identity feature was added in Ruby 1.9.2, so this function won't work in earlier versions. I think mu is too short's answer is better.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文