IequalityComperer и странные результаты
-
29-09-2019 - |
Вопрос
Посмотрите на этот класс:
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();
}
}
У меня также есть векторный класс, который является просто двумя точками и некоторые другие атрибуты. Я не хочу иметь равные очки в моем векторе, поэтому я придумал этот метод:
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, я записал эти две точки, будет иметь один и тот же Hashcode.
Может ли кто-нибудь объяснить мне, почему это не работает, как я зарубей это? Я не уверен, что объяснил это хорошо, но .. Я ценю помощь: D
Джордж
Решение
Вы реализуете IEqualityComparer<T>
В том числе он пытается сравнить - что очень странно. Вы должны почти наверняка просто реализовать IEquatable<T>
и переопределение Equals(object)
вместо. Это определенно сделает вашу единицу тестируемой работой.
Разница между IEquatable<T>
и IEqualityComparer<T>
это то, что первое реализуется классом, чтобы сказать: «Я могу сравнить сам с другим экземпляром того же типа ». (Это не имеют быть тем же типом, но обычно это.) Это уместно, если есть естественное сравнение - например, сравнение, выбранное string
является порядковым равенством - это должно быть точно та же последовательность char
ценности.
Теперь IEqualityComparer<T>
отличается - это может сравнить любые два экземпляра типа. Там могут быть несколько различных реализаций этого для данного типа, поэтому не имеет значения, является ли особая сравнение «естественным» - просто нужно быть правильным для вашей работы. Так например, вы могли бы иметь Shape
Класс, и другое равенство сравнивает сравнивать фигуры по цвету, области или что-то в этом роде.
Другие советы
Вам нужно переопределить 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);
}
Вам также нужно переосмыслить вашу концепцию «равенства», поскольку она в настоящее время не встречается .NET рамки требования.
Если вообще возможно, я рекомендую повторную конструкцию с репозиторием мемориальных точек объектов (возможно, ключом по имени), так что можно использовать простое ссылочное равенство.
Вы положили тег ArcObjects на это, поэтому я просто подумал, что упомянул IrelationalationoPerator.equals.. Отказ Я никогда не тестировал, чтобы увидеть, если этот метод отличится в толерании кластера пространственных ссылок на геометрию. Это может быть отрегулировано с использованием IspatialreferenceTolerance.xytolerance..