我有一个Point3d
结构,它以以下方式实现IEquatable<Point3d>
:
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);
}
这已在应用程序中大量使用,期望检查两点之间的相等性可以容忍(这对于实现正确工作是必不可少的。)
如果我注意到Equals
和GetHashCode
方法不一致,并且实际上不可能以会产生良好且一致的结果的形式编写GetHashCode
。在使用Linq查询(例如points.Distinct()
)的情况下,此问题尤其成问题,因为如果直接比较,例如points[0] == points[1]
我个人认为最好的选择是按照以下方式更改Equals
,以使其行为与GetHashCode
保持一致:
public bool Equals(Point3d other) {
return Equals(other, 0);
}
但是,由于这已在应用程序中大量使用,这将是一个重大的突破性变化。我认为这是错误的做法,但是我正在考虑将GetHashCode
更改为:
public override int GetHashCode() {
return 0;
}
我的理解是,以上内容将强制使用Equals
方法,这将导致性能下降,但同时允许在Linq查询中将容差内的点视为相等。我想知道这是否给我带来了其他潜在的陷阱。
我不确定我还可以使用哪些其他途径,所以我非常在意关于解决此问题的最佳方法的建议。
谢谢!
答案 0 :(得分:0)
一个痛苦的事实是,您不能使用任意的Equals
实现正确的tolerance
。
Equals
(有关详细信息,请参见https://msdn.microsoft.com/en-us/library/336aedhh(v=vs.100).aspx)
是 transitive ,即(x.Equals(y) && y.Equals(z))
返回true
当且仅当 x.Equals(z)
返回true
时。
在这里,我们可以为给定的Tolerance.ToleranceDecimal
创建一个计数器示例:
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
但是
x.Equals(z) == false
由于Equals
实现不正确,我们无法创建相应的GetHashCode
,除了 degeneration (和 useless )
public override int GetHashCode() {
return 0;
}
因为GetHashCode
必须返回x
的相同值,如果返回y
,则必须返回x.Equals(y) == true
。在我们的示例中:让x < y
和y = x + N * tolerance
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
,任何参数都必须返回相同的值。