Гарантированный способ сравнения объектов по идентичности в Ruby - PullRequest
14 голосов
/ 25 октября 2011

Каков гарантированный способ сравнения двух объектов по их идентичности в 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 ]

13 голосов
/ 25 октября 2011

Вы можете получить несвязанную версию 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 все равно будет исходным правильным.

Так что этот обходной хак дает вам надежный object_id для любого объекта (с учетом предостережений выше).Теперь вы можете получить и сравнить идентификаторы объектов, чтобы получить надежное сравнение.

6 голосов
/ 07 марта 2013

Используйте BasicObject#equal?.Согласно Ruby документации :

В отличие от ==, равно?метод никогда не должен быть переопределен подклассами: он используется для определения идентичности объекта (то есть a.equal? ​​(b) если a является тем же объектом, что и b).

Нужна более надежная гарантия?AFAIK не может получить его, BaseObject#object_id, ObjectSpace._id2ref, Hash#compare_by_identity может быть отменено или исправлено обезьяной (по крайней мере, это мое личное убеждение).

3 голосов
/ 26 октября 2011

Ответ mu слишком короткий - отлично, но есть другой способ сделать это с помощью специальной функции класса Hash:

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

Функция compare_by_identity была добавлена ​​в Ruby 1.9.2, поэтому эта функция не будет работать в более ранних версиях. Я думаю мю слишком короток ответ лучше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...