c# - IEquatable在公差范围内,如何实现 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);
}
这已经在应用程序中大量使用,期望检查两点之间的相等性允许容差(这是实现正常工作所必需的)。
如果我注意到Equals
和GetHashCode
方法不一致,并且确实不可能以GetHashCode
会产生良好且一致的结果的形式编写。这个问题在使用 Linq 查询的情况下尤其成问题,例如points.Distinct()
如果直接比较结果点可能被认为是 Equal,例如points[0] == points[1]
我个人认为最好的选择是进行Equals
如下更改,使其行为与以下内容一致GetHashCode
:
public bool Equals(Point3d other) {
return Equals(other, 0);
}
然而,由于这已经在应用程序中大量使用,这将是一个重大的突破性变化。我认为这是错误的做法,但我正在考虑GetHashCode
改为:
public override int GetHashCode() {
return 0;
}
我的理解是,上述内容将强制Equals
使用该方法,这将导致性能下降,但也允许在 Linq 查询中将容差内的点视为相等。我想知道这是否会让我发现任何其他潜在的陷阱。
我不确定还有哪些其他途径可供我使用,因此我非常想就解决此问题的最佳方法寻求建议。
提前致谢!
解决方案
苦涩的事实是,您无法Equals
使用任意实现正确的tolerance
。
Equals
(有关详细信息,请参阅https://msdn.microsoft.com/en-us/library/336aedhh(v=vs.100).aspx)必须是可传递的,即当且仅当(x.Equals(y) && y.Equals(z))
返回时返回。true
x.Equals(z)
true
在这里我们可以为 given创建一个反例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
的,除了退化(和无用)
public override int GetHashCode() {
return 0;
}
因为和ifGetHashCode
必须返回相同的值。在我们的例子中:让和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
必须为任何参数返回相同的值。
推荐阅读
- java - 使用 Spring restTemplate.exchange 时如何设置 HTTP 协议版本?
- reactjs - React:TypeError:无法将 null 转换为对象
- mysql - MySQL触发器跨表匹配字段
- c# - C# 插值字符串中的变量求值顺序
- haskell - 使用 Servant 根据 `Maybe` 的内容回答 200 或 404 的请求
- visual-studio-code - 创建兼容 ssh 的 vscode 扩展
- jquery - 按钮只允许点击一次
- python - 一个视图中的 Django 多个 GET 请求
- c - 可以用光线追踪器渲染莫比乌斯带吗?
- signals - Simulink,生成多项式输入信号