2010-02-07 2 views
2

Я новичок в C#. Возможно, я не правильно реализую IEquatable, потому что объекты моего типа, которые следует считать одинаковыми, не являются.C# XNA: проблема со словарями

Класс:

class CompPoint : IComparable { 
     public int X; 
     public int Y; 

     public CompPoint(int X, int Y) { 
      this.X = X; 
      this.Y = Y; 
     } 

    public override bool Equals(Object o) { 
     if (!(o is CompPoint)) { 
      throw new ArgumentException(String.Format("argument is not a CompPoint (%s given)", o)); 
     } 
     CompPoint cp = (CompPoint)o; 

     return this.X == cp.X && this.Y == cp.Y; 
    } 

    public override int GetHashCode() { 
     int hash = base.GetHashCode(); // this is a problem. replace with a constant? 
     hash = (hash * 73) + this.X.GetHashCode(); 
     hash = (hash * 73) + this.Y.GetHashCode(); 
     return hash; 
    } 
} 

(. Существует более CompPoint, чем это, что делает его оправдание есть класс)

Затем этот тест не пройден:

[TestMethod()] 
    public void compPointTest() { 
     Assert.AreEqual(new CompPoint(0, 0), new CompPoint(0, 0)); 
    } 

Что я Я не понимаю? Является ли Assert.AreEqual() использованием ссылочного равенства? Является ли моя функция Equals() в CompPoint испорченной?

Эта функция также не:

public void EqualsTest() { 
     Assert.IsTrue(new CompPoint(1, 1).Equals(new CompPoint(1, 1))); 
    } 

Эта причина этого заключается в том, что я использую Dictionary, и он не работает так, как я надеюсь, что это будет:

[TestMethod()] 
    public void dictCompPointTest() { 
     IDictionary<CompPoint, int> dict = new Dictionary<CompPoint, int>(); 
     dict[new CompPoint(0, 0)] = 4; 
     dict[new CompPoint(0, 0)] = 24; 
     dict[new CompPoint(0, 0)] = 31; 

     Assert.AreEqual(31, dict[new CompPoint(0, 0)]); 
     Assert.AreEqual(1, dict.Count); 
    } 

тест не с этим сообщением:

Метод испытания ShipAILabTest.BoardUtilsTest.dictCo mpPointTest исключение: System.Collections.Generic.KeyNotFoundException: Данный ключ отсутствует в словаре .

Этот тест инкапсулирует мои ожидания. Я надеюсь, что, поскольку ключ идентичен каждый раз, значение будет перезаписано. Что такое Dictionary, чтобы проверить равенство?

UPDATE: Я добавил функцию равенства, согласно предложению Томаса, и теперь CompPoint сравнительные тесты работают, и dictCompPointTest работы.

public override bool Equals(Object o) { 
     if (!(o is CompPoint)) { 
      throw new ArgumentException(String.Format("argument is not a CompPoint (%s given)", o)); 
     } 
     CompPoint cp = (CompPoint)o; 

     return this.X == cp.X && this.Y == cp.Y; 
    } 

Загадочно, этот тест до сих пор не удается:

[TestMethod()] 
    public void dictCPTest2() { 
     IDictionary<CompPoint, int> dict = new Dictionary<CompPoint, int>(); 
     dict[new CompPoint(2, 2)] = 2; 
     dict[new CompPoint(2, 2)] = 2; 

     Assert.AreEqual(1, dict.Count); 
    } 

тест также терпит неудачу, когда ключи new CompPoint(4, 1), но не тогда, когда ключи new CompPoint(0, 1). Почему это может работать для некоторых ценностей, а не для других?

Больше загадок: функция хеш-кода, похоже, работает довольно плохо. Этот тест не выполнен:

[TestMethod()] 
    public void hashCodeTest() { 
       int x = 0; 
       int y = 0; 
       Assert.AreEqual(new CompPoint(x, y).GetHashCode(), new CompPoint(x, y).GetHashCode());  
    } 

Функция хеш-кода приведена выше. В чем проблема? Не должны ли два объекта CompPoint иметь один и тот же хэш-код? Может быть, мой вызов base.getHashCode() является проблемой?

ответ

1

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

public class CompPoint : IEquatable<CompPoint> 
{ 
    // ... 

    public override bool Equals(object obj) // object 
    { 
     return this.Equals(obj as ComPoint); 
    } 

    public bool Equals(CompPoint other) // IEquatable<ComPoint> 
    { 
     return !object.ReferenceEquals(other, null) 
      && this.X.Equals(other.X) 
      && this.Y.Equals(other.Y); 
    } 

    public override int GetHashCode() // object 
    { 
     int hash = 5419; 
     hash = (hash * 73) + this.X.GetHashCode(); 
     hash = (hash * 73) + this.Y.GetHashCode(); 
     return hash; 
    } 
} 
+0

Это интригующе, но в конечном счете бесплодно. «Словарь» по-прежнему не может обнаружить дубликаты. –

+0

Я думаю, что 'getHashCode()' может вызвать проблему. (см. выше) –

+1

Не знаете, исправлено это или нет, но если ваш метод GetHashCode по-прежнему является тем, что вы разместили в приведенном выше коде, он все равно будет генерировать другое для двух экземпляров (то есть два новых CompPoint (0, 0)) из-за их вызова base.GetHashCode() – jeffora

3

Я думаю, Assert.AreEqual просто использует Object.Equals, а не IEquatable<T>.Equals. Поэтому вам необходимо переопределить Equals, чтобы отразить логику IEquatable<T>.Equals.

Или вы можете также использовать Assert.IsTrue:

IEquatable<CompPoint> p1 = new CompPoint(0, 0); 
IEquatable<CompPoint> p2 = new CompPoint(0, 0); 
Assert.IsTrue(p1.Equals(p2)); 

Обратите внимание, что я объявляю p1 и p2, как IEquatable<CompPoint>: это то, чтобы гарантировать, что IEquatable<CompPoint>.Equals называется, а не Object.Equals, поскольку интерфейс реализован явно

EDIT: Кстати, вы можете объявить CompPoint как структуру, а не класс. Таким образом, вам даже не нужно ничего реализовывать, поскольку значения типов сравниваются в соответствии с их значениями поля.

+0

+1. Ударь меня к удару. Вместо того, чтобы «Equals()» дублировать логику, просто попросите его вызвать другой метод. –

+0

Обновлен мой вопрос. Важно ли использовать 'IEquatable ' как тип, а не просто 'CompPoint'? –

+0

Это важно, потому что вы внедрили 'IEquatable ' ** явно **. Однако я не понимаю, почему он не работает, когда вы используете свой тип в качестве словарного ключа: S –

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