C# IEquatable<;Point3D>;在公差范围内,如何实现GetHashCode()

C# IEquatable<;Point3D>;在公差范围内,如何实现GetHashCode(),c#,.net,gethashcode,iequatable,C#,.net,Gethashcode,Iequatable,我有一个Point3d结构,它以以下方式实现IEquatable: public override bool Equals(object obj) { return obj is Point3d p && Equals(p); } public bool Equals(Point3d other) { return Equals(other, Tolerance.ToleranceDecimal); } public bool Equals(Point3d o

我有一个
Point3d
结构,它以以下方式实现
IEquatable

public override bool Equals(object obj) {
    return obj is Point3d p && Equals(p);
}

public bool Equals(Point3d other) {
    return Equals(other, Tolerance.ToleranceDecimal);
}

public bool Equals(Point3d other, double tolerance) {
    if (tolerance < 0) throw new ArgumentOutOfRangeException(nameof(tolerance), tolerance, "Expected a tolerance greater than or equal to 0");
    return Math.Abs(X - other.X) <= tolerance && Math.Abs(Y - other.Y) <= tolerance && Math.Abs(Z - other.Z) <= tolerance;
}

public override int GetHashCode() {
    var hash = 17;
    hash = hash * 23 + X.GetHashCode();
    hash = hash * 23 + Y.GetHashCode();
    hash = hash * 23 + Z.GetHashCode();
    return hash;
}

public static bool operator ==(Point3d firstPoint, Point3d secondPoint) {
    return firstPoint.Equals(secondPoint);
}

public static bool operator !=(Point3d firstPoint, Point3d secondPoint) {
    return !(firstPoint == secondPoint);
}
然而,由于这已经在应用程序中大量使用,这将是一个重大的突破性变化。我认为这样做是错误的,但我正在考虑将
GetHashCode
更改为:

public override int GetHashCode() {
    return 0;
}
我的理解是,上述情况将强制使用
Equals
方法,这将导致性能下降,但也允许在Linq查询中将公差范围内的点视为相等。我想知道这是否会让我面临任何其他潜在的陷阱

我不确定还有什么其他途径可供我使用,所以我非常希望得到关于解决这个问题的最佳方法的建议


提前谢谢

痛苦的事实是,你不能用任意的
公差实现正确的
等于
等于
(有关详细信息,请参阅)必须 可传递,即
(x.Equals(y)和&y.Equals(z))
返回
true
当且仅当
x.Equals(z)
返回
true

在这里,我们可以为给定的公差创建一个反例。公差十进制

 Point3d x = new Point3d(-Tolerance.ToleranceDecimal * 2.0 / 3.0, 0, 0);
 Point3d y = new Point3d(0, 0, 0);
 Point3d z = new Point3d(Tolerance.ToleranceDecimal * 2.0 / 3.0, 0, 0);
如你所见

 x.Equals(y) == true
 y.Equals(z) == true
但是

由于
Equals
实现不正确,我们无法创建相应的
GetHashCode
,但退化(且无用)除外

因为
GetHashCode
必须为
x
y
返回相同的值,如果
x.Equals(y)==true
。在我们的例子中:让
x
y=x+N*公差

 x equals to 
 x + tolerance / 2.0 equals to
 x + tolerance / 2.0 * 2 equals to
 x + tolerance / 2.0 * 3 equals to
 ...
 x + tolerance / 2.0 * 2 * N equals to
 y

这意味着对于任意的
x
y
和非零公差
GetHashCode
必须为任何参数返回相同的值。

请看这里:好的,我得到一条消息,我应该更改equals方法,使其不允许公差。我仍然缺少的是让GetHashCode只返回0并强制使用带有容差的Equals方法的缺点(除性能外)。@RobKite当您违反该接口的约定时,具体会发生什么完全取决于您使用它做什么。使用该接口的不同代码将对它们执行完全不同的操作,因此当您违反接口的约定时,将产生完全不同的效果。大多数也不会意外地崩溃。当您提供的输入违反了它们的约定时,它们的结果通常是未定义的。@Servy您能举一个例子说明它可以做什么,除了在dictionary中使用performance hit=O(n)之外?我想不出有什么情况会给你不正确的答案results@MistyKOP没有提到在字典中使用它。也就是说,最终可能会有多个不同的相等对象作为键,但具有不同的值,根据执行给定操作的顺序,不同操作会得到不同的结果(以不同的顺序将相同的项添加到字典会更改生成的字典),等等。谢谢大家快速简洁的回答。@Rob Kite:不客气!谢谢你的非常清晰和有价值的帖子。。。但是:是的。。。这是完全正确的。。。这让我开始探索我的一些代码。。。在读了这篇文章和MSDN链接之后,我也读了。这里,Microsoft self介绍了用于确定浮点相等性的公差的引入。所以我们不能容忍平等,但我们应该容忍平等。。。euh?@SvenL:比较(请注意,只是比较,不实现自定义的
Equals
GetHashCode
方法)两个带容差的浮点值是一个很好的实践:
if(Math.Abs(left-right)
我完全同意,但是,当实现诸如Vector2D、Point或Coordinate之类的类型时(其核心是浮点值),容错相等(有时)是必要的。有时在Equals中允许这样做会很好,所以框架会将这些值视为Equals。。。但我明白你的意思,所以你的帖子把我送回了画板上,重新思考我采取的方法。
 x.Equals(z) == false
 public override int GetHashCode() {
   return 0;
 }
 x equals to 
 x + tolerance / 2.0 equals to
 x + tolerance / 2.0 * 2 equals to
 x + tolerance / 2.0 * 3 equals to
 ...
 x + tolerance / 2.0 * 2 * N equals to
 y