2016-08-10 8 views
2

Согласно this article, я сопровождал свой Equal (Thing) следующим образом.StackOverflow при проектировании операторов сравнения

public override int GetHashCode() { return Id.GetHashCode(); } 

public override bool Equals(object input) 
{ 
    Thing comparee = input as Thing; 
    return comparee != null && comparee.Id == Id; 
} 

public static bool operator ==(Thing self, Thing other) 
{ 
    return self != null && other != null && self.Id == other.Id; 
} 

public static bool operator !=(Thing self, Thing other) 
{ 
    return self == null || other == null || self.Id != other.Id; 
} 

Проблема в том, что, пока она работала до того, как я добавил переопределения оператора, теперь я получаю исключение StackOverflowException. Что мне не хватает?

ответ

3

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

public static bool operator ==(Thing self, Thing other) 
{ 
    return !ReferenceEquals(self, null) && 
      !ReferenceEquals(other, null) && 
      self.Id == other.Id; 
} 

public static bool operator !=(Thing self, Thing other) 
{ 
    return !(self == other); 
} 

Использование ссылок равное не вызовет переполнение стека исключение, так как оно не использует операторов ==/!=, а реализация != для простого возврата ! оператора == сохранит ваше обслуживание в случае, если тест равенства будет когда-либо изменен. Это реализация принципа DRY.

+0

Я вижу, как гладкость меняется только в одном месте. Однако как вы относитесь к * возврату ReferenceEquals (self, other) || self? .Id == other? .Id; * для равенства и * return! ReferenceEquals (self, other) || self? .Id! = other? .Id; * для неравенства? –

+0

Я думаю, что, несмотря на то, что вы решили реализовать оператор равенства, вам все равно лучше реализовать не равный оператор, просто вернув не оператор равенства. Вот в чем смысл моего ответа здесь: не реализуйте дважды то, что вы можете реализовать только один раз. –

+0

@ KonradViltersten: Похоже, ваша логика немного испортилась. '! a || b' - это не то же самое, что '! (a || b)'. (Причина № 7, почему лучше иметь логику в одном месте и все это ссылается на нее. :) – cHao

5

Как только вы определяете оператора ==, который сравнивает Thing s, это будет использоваться, если вы скажете someThing == null. Аналогично с !=.

Так что если вы скажете self == other, вы в конечном итоге звоните operator ==(self, other). Среди ваших критериев у вас есть self != null, который вызывает operator !=(self, null) ... который проверяет, self == null, таким образом, вызывая operator ==(self, null), и круглый и круглый вы идете до тех пор, пока вы не закончите пространство стека.

Я уверен, что вы можете обойти это, литая материал до object для сравнения. Или, вы можете сказать Object.ReferenceEquals(self, null) и т. Д., Который не полагается на ==, поэтому вы не получите рекурсию.