IEquatable<Point3D>在公差范围内,如何实现GetHashCode()
IEquatable<Point3D> within a tolerance, how to implement GetHashCode()
我有一个 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);
}
这已经在应用程序中大量使用,期望检查两点之间的相等性允许容差(这是实现正常工作所必需的)。
If 注意到 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 查询中将公差范围内的点视为相等。我想知道这是否让我面临任何其他潜在的陷阱。
我不确定还有哪些其他途径可供我使用,因此我非常希望就解决此问题的最佳方法寻求建议。
提前致谢!
苦涩的事实是你不能用任意tolerance
实现正确的Equals
。
Equals
(详见https://msdn.microsoft.com/en-us/library/336aedhh(v=vs.100).aspx)必须
传递即(x.Equals(y) && y.Equals(z))
returnstrue
当且仅当x.Equals(z)
returns 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
,除了 degenerated(和 useless)
public override int GetHashCode() {
return 0;
}
因为 GetHashCode
必须 return 相同的 值 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
必须 return 任何参数的相同值。
我有一个 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);
}
这已经在应用程序中大量使用,期望检查两点之间的相等性允许容差(这是实现正常工作所必需的)。
If 注意到 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 查询中将公差范围内的点视为相等。我想知道这是否让我面临任何其他潜在的陷阱。
我不确定还有哪些其他途径可供我使用,因此我非常希望就解决此问题的最佳方法寻求建议。
提前致谢!
苦涩的事实是你不能用任意tolerance
实现正确的Equals
。
Equals
(详见https://msdn.microsoft.com/en-us/library/336aedhh(v=vs.100).aspx)必须
传递即(x.Equals(y) && y.Equals(z))
returnstrue
当且仅当x.Equals(z)
returns 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
,除了 degenerated(和 useless)
public override int GetHashCode() {
return 0;
}
因为 GetHashCode
必须 return 相同的 值 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
必须 return 任何参数的相同值。