简体   繁体   English

IEqualityComparer和奇怪的结果

[英]IEqualityComparer and weird results

Take a look at this class: 看一看这个课程:

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();
    }
}

I also have a Vector class, which is merely two points and some other atributes. 我也有一个Vector类,它只有两点和其他一些属性。 I don't want to have equal points in my Vector, so I came up with this method: 我不想在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;
        }

And this unit test, which should give me an exception: 而这个单元测试,应该给我一个例外:

    [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);
    }

When i use the same point, that is, mPoint1, as in the code the exception is thrown. 当我使用同一点(即mPoint1)时,如代码中所示,将引发异常。 When I use mPoint2, even their name and coordinates being the same, the exception is not thrown. 当我使用mPoint2时,即使它们的名称和坐标相同,也不会引发异常。 I checked their hash codes, and they are in fact different. 我检查了他们的哈希码,实际上它们是不同的。 Based on the code I created in GetHashCode, I tought these two point would have the same hashcode. 基于我在GetHashCode中创建的代码,我坚称这两点将具有相同的哈希码。

Can someone explain to me why this is not working as I tought it would? 有人可以向我解释为什么这不起作用吗? I'm not sure I explained this well, but.. I appreciate the help :D 我不确定我解释得很好,但是..我感谢您的帮助:D

George 乔治

You're implementing IEqualityComparer<T> within the type it's trying to compare - which is very odd. 您正在尝试比较的类型内实现IEqualityComparer<T> -这很奇怪。 You should almost certainly just be implementing IEquatable<T> and overriding Equals(object) instead. 几乎可以肯定,您应该只是实现IEquatable<T>并改写Equals(object) That would definitely make your unit test work. 那肯定会使您的单元测试工作。

The difference between IEquatable<T> and IEqualityComparer<T> is that the former is implemented by a class to say, "I can compare myself with another instance of the same type." IEquatable<T>IEqualityComparer<T>之间的区别在于,前者是由一个类实现的,它表示“我可以将自己与另一个相同类型的实例进行比较”。 (It doesn't have to be the same type, but it usually is.) This is appropriate if there's a natural comparison - for example, the comparison chosen by string is ordinal equality - it's got to be exactly the same sequence of char values. (它不必是同一类型,但它通常是。)这是合适的,如果有一个自然的比较-例如,通过选择比较string是有序的平等-这一定是完全相同的同一序列char值。

Now IEqualityComparer<T> is different - it can compare any two instances of a type. 现在IEqualityComparer<T>有所不同-它可以比较类型的任何两个实例。 There can be multiple different implementations of this for a given type, so it doesn't matter whether or not a particular comparison is "the natural one" - it's just got to be the right one for your job. 对于给定的类型,可以有多种不同的实现方式,因此,特定的比较是否为“自然比较”并不重要-只是适合您的工作。 So for example, you could have a Shape class, and different equality comparers to compare shapes by colour, area or something like that. 因此,例如,您可以拥有一个Shape类,并使用不同的相等比较器来按颜色,面积或类似方式比较形状。

You need to override Object.Equals as well. 您还需要重写Object.Equals

Add this to your implementation: 将此添加到您的实现中:

// 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;
}

I'd then rework your other implementation to use the first, plus add the appropriate null checks. 然后,我会重做您的其他实现以使用第一个实现,并添加适当的null检查。

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

You also need to rethink your concept of "equality", since it's not currently meeting .NET framework requirements . 您还需要重新考虑“平等”的概念,因为它目前不满足.NET Framework 要求

If at all possible, I recommend a re-design with a Repository of memorial point objects (possibly keyed by name), so that simple reference equality can be used. 如果可能的话,我建议重新设计一个带有纪念点对象的存储库(可能用名称作为关键字),以便可以使用简单的引用相等性。

You've put an arcobjects tag on this, so I just thought I'd mention IRelationalOperator.Equals . 您已经在此对象上放置了一个arcobjects标记,所以我以为我要提到IRelationalOperator.Equals I've never tested to see if this method honors the cluster tolerance of the geometries' spatial references. 我从未测试过这种方法是否符合几何空间参考的簇公差。 This can be adjusted using ISpatialReferenceTolerance.XYTolerance . 可以使用ISpatialReferenceTolerance.XYTolerance进行调整。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM