2009-12-19 3 views
38

Для простой структуры типа класса:Что такое правильный способ реализации равенства в рубине

class Tiger 
    attr_accessor :name, :num_stripes 
end 

что правильный способ правильно осуществить равенство, чтобы гарантировать, что ==, ===, eql? и т.д. работа, и так что экземпляры класса дружат в наборах, хэши и т.д.

EDIT

Кроме того, что хороший способ для реализации равенства, когда вы хотите сравнить на основе по состоянию, которое не отображается вне класса? Например:

class Lady 
    attr_accessor :name 

    def initialize(age) 
    @age = age 
    end 
end 

здесь я хотел бы мой метод равенства принять @age во внимание, но леди не подвергает ее возраст клиентов. Должен ли я использовать instance_variable_get в этой ситуации?

+0

[Это хорошая рецензия сравнения входов и выходов, определяющее объектого равенства] (http://www.skorks.com/2009/09/ruby-equal-and-object-comparison /) – ennuikiller

ответ

61

Чтобы упростить операции сравнения для объектов с более чем одной переменной состояния, создайте метод, который возвращает все состояние объекта в виде массива. Тогда просто сравнить два состояния:

class Thing 

    def initialize(a, b, c) 
    @a = a 
    @b = b 
    @c = c 
    end 

    def ==(o) 
    o.class == self.class && o.state == state 
    end 

    protected 

    def state 
    [@a, @b, @c] 
    end 

end 

p Thing.new(1, 2, 3) == Thing.new(1, 2, 3) # => true 
p Thing.new(1, 2, 3) == Thing.new(1, 2, 4) # => false 

Кроме того, если вы хотите экземпляры вашего класса, который будет использоваться в качестве ключа хэша, а затем добавить:

alias_method :eql?, :== 

    def hash 
    state.hash 
    end 

Они должны быть открытыми.

+2

Мне очень нравится этот трюк сравнения объектов с помощью делегирования сравнения с массивом состояний. –

1

Обычно с оператором ==.

def == (other) 
    if other.class == self.class 
    @name == other.name && @num_stripes == other.num_stripes 
    else 
    false 
    end 
end 
+0

Спасибо. Однако я думаю, что если вы только определяете ==, то экземпляры класса будут вести себя не так, как ожидалось, в хэшах и наборах. –

+0

Кроме того, я/думаю/что == не должен проверять равенство типов (как показывает ваш пример). Вот что такое eql? должен делать. Возможно, это неправильно. –

+0

Поведение меняется только в том случае, если вы меняете его, Пит. Последнее, что я проверил «true == true» (и «1 + 1 == 2'), по-прежнему возвращает« true »... –

12

Чтобы проверить все ваш переменный экземпляр равенства сразу:

def ==(other) 
    other.class == self.class && other.state == self.state 
end 

def state 
    self.instance_variables.map { |variable| self.instance_variable_get variable } 
end 
+3

Это действительно лучше, потому что это долгий путь для обеспечения того, чтобы вы не случайно не оставляли никаких переменных. Теперь также могут быть непредвиденные последствия автоматического использования каждой переменной экземпляра, но я не уверен и не буду знать, пока это не удивит меня однажды. – WattsInABox

+0

Это правда, вы можете добавить необязательные параметры, такие как «только» или «исключение» для этих конкретных случаев. – jvenezia

Смежные вопросы