2009-05-27 6 views
3

Выполните следующие испытания класса и единицы измерения.Почему этот тест на равенство объекта терпит неудачу?

public class Entity 
    { 
     public object Id { get; set; } 

     public override bool Equals(object obj) 
     { 
      return this == (Entity)obj; 
     } 

     public static bool operator == (Entity base1, Entity base2) 
     { 
      if (base1.Id != base2.Id) 
      { 
       return false; 
      } 

      return true; 
     } 

     public static bool operator != (Entity base1, Entity base2) 
     { 
      return (!(base1.Id == base2.Id)); 
     } 
    } 

 [TestMethod] 
     public void Test() 
     { 
      Entity e1 = new Entity { Id = 1 }; 
      Entity e2 = new Entity { Id = 1 }; 
      Assert.IsTrue(e1 == e2); //Always fails 
     } 

Может кто-то объяснить, почему его не удается?

ответ

3

Потому что вы полагаетесь на ссылку на объект для сравнения:

public object Id { get; set; } 

Заменить

public static bool operator == (Entity base1, Entity base2) 
    { 
     if (base1.Id != base2.Id) 
     { 
      return false; 
     } 

     return true; 
    } 

С

public static bool operator == (Entity base1, Entity base2) 
    { 
     return object.Equals(base1.Id, base2.Id); 
    } 
+0

Я предпочитаю object.Equals (base1.Id, base2.Id), чтобы справиться с нулевыми идентификаторами. –

+0

Хорошее предложение. –

+0

(Он все еще оставляет отверстие, когда base1 или base2 является нулевым, конечно, но это также идет на другие проблемы :) –

2

Потому что e1.Id и e2.Id - разные объекты. Хотя они имеют одинаковое значение, они не являются одним и тем же объектом, поэтому base1.Id == base2.Id терпит неудачу.

+0

Хорошо - если я изменю свой идентификатор на тип int, он должен работать ... Разве это невозможно переопределить == по типам объектов? – 2009-05-27 16:12:01

+1

Вы никогда не * переопределяете * операторы, вы только * перегружаете их. Обычно вы переопределяете метод Equals. –

+0

Это потому, что классы времени компиляции Id являются объектом, поэтому он использует стандартное ссылочное равенство. == и! = не являются виртуальными в том, как вы ожидаете, метод == используется для определения compile время – thecoop

1

Потому что ваше свойство Id является объектом.

1 (как int) помещается в объект кучи, но каждый 1 помещается в отдельный экземпляр. Поскольку Id является объектом, условие base1.Id! = Base2.Id проверяет значение . Ссылка равенство, а не значение равенство, которое вы хотите. Изменение идентификатора в int или использование Equals(), а не! = Должно исправить это.

2

Ваша реализация equals - это просто сравнение ссылок, а не содержимого объекта. Вы по существу сравниваете два указателя, и поскольку вы создали два отдельных объекта, ваше равенство потерпит неудачу.

Есть несколько статей о том, как правильно реализовать Equals, вот начало:

http://weblogs.asp.net/tgraham/archive/2004/03/23/94870.aspx

Для объектов базы данных, вы можете сделать ярлык Равно реализации, просто сравнивая идентификаторы баз данных, предполагая, что два объекта с тем же идентификатором считаются «равными» в вашей системе.

2

Ваш Идентификатор - это объект, а не int. Для объектов оператор == не проверяет равенство значений.

8

Ваш Id Недвижимость типа object. Когда вы создаете два экземпляра с использованием 1 в качестве идентификатора для каждого из них, вы получите два разных объекта в коробке. Затем вы сравниваете эти объекты, используя ссылка равенство.

Предлагаемые изменения, чтобы исправить это:

  • Изменить тип Id быть типа int, если это необходимо.
  • Используйте статический метод object.Equals сравнить Идентификаторы вместо ==

Любой из них будет работать, но первое предпочтительнее ИМО.

Существуют различные способы, в которых реализация может быть сделана более чистой, если вы заинтересованы, но я понимаю, что это может быть просто фиктивный пример. Только быстрый список на первый взгляд:

  • Вы должны быть наиважнейшая GetHashCode, а также Equals.
  • Ваш Equals переопределение не должно выполнять бросок безоговорочно, поскольку это вызовет исключение, если объект имеет неправильный тип. Если тип неверен, верните false.
  • Ваше текущее выполнение == может быть упрощена только

    return base1.Id == base2.Id; 
    
  • Ваша реализация == должен выполнять ничтожности проверки
  • Это вообще лучше реализовать! = Вернув !(base1 == base2), если вы не хотите специалиста поведения.
  • Переопределение Equals в незапечатанном классе может быть проблематичным. Если вы не планируете наследование, стоило бы запечатать класс (IMO - это, вероятно, будет спорным).
+0

Причина, по которой я был вызван этой проблемой, состоял в том, что я пытался использовать Entity в словаре ... и получить ключ. Так я вынужден изменить тип на int, если я хочу использовать Entity в классе Dictionary? - – 2009-05-27 16:26:08

+0

Если вы пытаетесь использовать его в качестве словарного ключа, вы должны * определенно * переопределять GetHashCode. Это абсолютно * критическое *. Нет, вы не должны изменять тип Id для int, но вы должны использовать Equals для сравнения идентификаторов, а не ==. Вы можете просто использовать hashcode идентификатора в вашей реализации GetHashCode. –

0

Если вы сделали свой Id членом int, это сработает. Но, как сказал CookieOfFortune, когда вы сравниваете два объекта, он смотрит, являются ли они одним и тем же объектом, а не имеют ли они одинаковое значение.

0

изменить тип Id к междунар или другое значение тип. Проблема в том, что вы сравниваете 2 объекта, которые, как я полагаю, является проблемой, которую вы перегрузили оператором ==, чтобы решить