IEqualityComparer и странные результаты - PullRequest
0 голосов
/ 24 июня 2010

Посмотрите на этот класс:

public class MemorialPoint:IMemorialPoint,IEqualityComparer<MemorialPoint>
{
    private string _PointName;
    private IPoint _PointLocation;
    private MemorialPointType _PointType;

    private DateTime _PointStartTime;
    private DateTime _PointFinishTime;

    private string _NeighborName;

    private double _Rms;
    private double _PointPdop;
    private double _PointHdop;
    private double _PointVdop;

    // getters and setters omitted

    public bool Equals(MemorialPoint x, MemorialPoint y)
    {
        if (x.PointName == y.PointName)
            return true;
        else if (x.PointName == y.PointName && x.PointLocation.X == y.PointLocation.X && x.PointLocation.Y == y.PointLocation.Y)
            return true;
        else
            return false;
    }

    public int GetHashCode(MemorialPoint obj)
    {
        return (obj.PointLocation.X.ToString() + obj.PointLocation.Y.ToString() + obj.PointName).GetHashCode();
    }
}

У меня также есть класс Vector, который представляет собой просто две точки и некоторые другие атрибуты. Я не хочу иметь равные точки в моем векторе, поэтому я выбрал этот метод:

public void RecalculateVector(IMemorialPoint fromPoint, IMemorialPoint toPoint, int partIndex)
        {
            if (fromPoint.Equals(toPoint))
                throw new ArgumentException(Messages.VectorWithEqualPoints);

            this.FromPoint = FromPoint;
            this.ToPoint = ToPoint;
            this.PartIndex = partIndex;

            // the constructDifference method has a weird way of working:
            // difference of Point1 and Point 2, so point2 > point1 is the direction
            IVector3D vector = new Vector3DClass();
            vector.ConstructDifference(toPoint.PointLocation, fromPoint.PointLocation);

            this.Azimuth = MathUtilities.RadiansToDegrees(vector.Azimuth);

            IPointCollection pointCollection = new PolylineClass();
            pointCollection.AddPoint(fromPoint.PointLocation, ref _missing, ref _missing);
            pointCollection.AddPoint(toPoint.PointLocation, ref _missing, ref _missing);

            this._ResultingPolyline = pointCollection as IPolyline;
        }

А это юнит-тест, который должен дать мне исключение:

    [TestMethod]
    [ExpectedException(typeof(ArgumentException), Messages.VectorWithEqualPoints)]
    public void TestMemoriaVector_EqualPoints()
    {
        IPoint p1 = PointPolygonBuilder.BuildPoint(0, 0);
        IPoint p2 = PointPolygonBuilder.BuildPoint(0, 0);

        IMemorialPoint mPoint1 = new MemorialPoint("teste1", p1);
        IMemorialPoint mPoint2 = new MemorialPoint("teste1", p2);

        Console.WriteLine(mPoint1.GetHashCode().ToString());
        Console.WriteLine(mPoint2.GetHashCode().ToString());

        vector = new MemorialVector(mPoint1, mPoint1, 0);
    }

Когда я использую ту же точку, то есть mPoint1, как в коде, генерируется исключение. Когда я использую mPoint2, даже если их имя и координаты совпадают, исключение не выдается. Я проверил их хэш-коды, и они на самом деле разные. Основываясь на коде, который я создал в GetHashCode, я решил, что эти две точки будут иметь одинаковый хэш-код.

Может кто-нибудь объяснить мне, почему это не работает, как я думал, что будет? Я не уверен, что объяснил это хорошо, но .. Я ценю помощь: D

George

Ответы [ 4 ]

4 голосов
/ 24 июня 2010

Вы реализуете IEqualityComparer<T> в типе, который он пытается сравнить - что очень странно. Вы почти наверняка просто реализуете IEquatable<T> и переопределяете Equals(object). Это определенно заставит ваш юнит-тест работать.

Разница между IEquatable<T> и IEqualityComparer<T> заключается в том, что первое реализовано классом, говорящим: «Я могу сравнить себя с другим экземпляром того же типа». (Это не имеет того же типа, но обычно это так.) Это уместно, если есть естественное сравнение - например, сравнение, выбранное string, является порядковым равенством - оно должно быть точно такой же последовательностью char значений.

Теперь IEqualityComparer<T> отличается - он может сравнивать любые два экземпляра типа. Может быть несколько разных реализаций этого для данного типа, поэтому не имеет значения, является ли конкретное сравнение «естественным» - оно просто должно быть правильным для вашей работы. Так, например, у вас может быть класс Shape и различные средства сравнения на равенство для сравнения форм по цвету, площади или что-то в этом роде.

1 голос
/ 25 июня 2010

Вы поместили тег arcobjects на это, поэтому я подумал, что упомяну IRelationalOperator.Equals . Я никогда не проверял, соблюдает ли этот метод кластерный допуск пространственных привязок геометрий. Это можно настроить с помощью ISpatialReferenceTolerance.XYTolerance .

1 голос
/ 24 июня 2010

Вам также необходимо переосмыслить концепцию «равенства», поскольку в настоящее время она не соответствует .NET framework требованиям .

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

1 голос
/ 24 июня 2010

Вам также необходимо переопределить Object.Equals.

Добавьте это к вашей реализации:

// In MemorialPoint:
public override bool Equals(object obj)
{
    if (obj == null || GetType() != obj.GetType()) 
         return false;

    MemorialPoint y = obj as MemorialPoint;

    if (this.PointName == y.PointName)
        return true;
    else if (this.PointName == y.PointName && this.PointLocation.X == y.PointLocation.X && this.PointLocation.Y == y.PointLocation.Y)
        return true;
    else
        return false;
}

Затем я бы переделал вашу другую реализацию, чтобы использовать первую, плюс добавил соответствующие нулевые проверки.

public bool Equals(MemorialPoint x, MemorialPoint y)
{
    if (x == null)
        return (y == null);
    return x.Equals(y);
}
...