IEqualityComparer 和奇怪的结果

发布于 2024-09-07 01:52:56 字数 3004 浏览 8 评论 0原文

看看这个类:

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

乔治

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. I don't want to have equal points in my Vector, so I came up with this method:

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. When I use mPoint2, even their name and coordinates being the same, the exception is not thrown. 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.

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

George

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

暖阳 2024-09-14 01:52:56

您正在尝试比较的类型中实现 IEqualityComparer - 这非常奇怪。几乎可以肯定,您应该只实现 IEquatable 并覆盖 Equals(object) 。这肯定会让你的单元测试工作。

IEquatableIEqualityComparer 之间的区别在于,前者由一个类实现,用于表示“我可以比较我自己”与相同类型的另一个实例。” (它不一定是相同的类型,但通常是相同的类型。)如果存在自然比较,则这是合适的 - 例如,由 string 选择的比较是序数相等 - 它必须是完全相同的 char 值序列。

现在,IEqualityComparer 有所不同 - 它可以比较某个类型的任意两个实例。对于给定类型,可以有多种不同的实现,因此特定比较是否是“自然的”并不重要 - 它必须是适合您工作的比较。例如,您可以有一个 Shape 类,以及不同的相等比较器来按颜色、面积或类似内容比较形状。

You're implementing IEqualityComparer<T> within the type it's trying to compare - which is very odd. You should almost certainly just be implementing IEquatable<T> and overriding Equals(object) instead. 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." (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.

Now IEqualityComparer<T> is different - it can compare any two instances of a type. 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.

梦罢 2024-09-14 01:52:56

您还需要重写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);
}

You need to override Object.Equals as well.

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.

public bool Equals(MemorialPoint x, MemorialPoint y)
{
    if (x == null)
        return (y == null);
    return x.Equals(y);
}
¢好甜 2024-09-14 01:52:56

您还需要重新考虑“平等”的概念,因为它目前不符合 .NET 框架要求

如果可能的话,我建议重新设计纪念点对象的存储库(可能按名称键入),以便可以使用简单的引用相等。

You also need to rethink your concept of "equality", since it's not currently meeting .NET framework requirements.

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.

苏璃陌 2024-09-14 01:52:56

你已经在上面放了一个 arcobjects 标签,所以我想我应该提到 IRelationalOperator.Equals。我从未测试过此方法是否遵守几何空间参考的簇容差。可以使用 ISpatialReferenceTolerance.XYTolerance 进行调整

You've put an arcobjects tag on this, so I just thought I'd mention 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文