【发布时间】:2011-07-21 10:48:35
【问题描述】:
我正在将 CodePlex 中的地理坐标类集成到我的个人“工具箱”库中。这个类使用float字段来存储经纬度。
由于GeoCoordinate类实现了IEquatable<GeoCoordinate>,所以我习惯性地这样写Equals方法:
public bool Equals(GeoCoordinate other)
{
if (other == null) {
return false;
}
return this.latitude == other.latitude && this.longitude == other.longitude;
}
此时我停下来并认为我正在比较浮点变量是否相等,这通常是一个禁忌。然后我的思考过程大致如下:
我只能想象设置
Latitude和Longitude属性一次,这意味着不会累积错误来搞乱我的比较。-
另一方面,可以(尽管毫无意义)写
var geo1 = new GeoCoordinate(1.2, 1.2); var geo2 = new GeoCoordinate(1.2, 1.2); // geo1.Equals(geo2) will definitely be true, BUT: geo2.Latitude *= 10; geo2.Latitude /= 10; // I would think that now all bets are off当然这不是我能想象的,但如果类的公共接口允许,那么
Equals应该能够处理它。 -
使用
difference < epsilon测试比较相等性可以解决比较两个实例的问题,但会产生更多问题:- 如何使相等传递?听起来不可能。
-
如何为比较相等的所有值生成相同哈希码?
假设
epsilon = 0.11(随机示例)。因此GeoCoordinate { 1, 1 }需要与GeoCoordinate { 1.1, 1.1 }相同的哈希码。但后者需要与GeoCoordinate { 1.2, 1.2 }相同的哈希码。您可以看到这是怎么回事:所有实例都需要具有相同的哈希码。
-
解决所有这些问题的方法是使
GeoCoordinate成为不可变类。这也将解决GetHashCode问题:它基于纬度和经度(还有什么),如果它们是可变的,那么使用GeoCoordinate作为字典的键是自找麻烦。然而,使类不可变有其自身的缺点:- 您无法实例化和配置类的实例(WPF 范例),这在某些情况下可能会很麻烦
- 由于丢失了无参数构造函数,序列化也可能会变得很痛苦(我不是 .NET 序列化专家,所以我在这里看到的细节就这么多)
您会建议哪种方法?使类符合我现在的要求很容易(只需使其不可变),但有更好的方法吗?
编辑:我在上面的列表中添加了第 3 项,将之前的第 3 项移动到位置 4。
解决方案
我将留出更多时间来提供反馈,但目前我将采用不可变的方法。相关成员的struct(因为现在是这样)可以看到here;非常欢迎 cmets 和建议。
【问题讨论】:
-
@ckeller:谢谢你的链接,我没找到。 Skeet 的解决方案几乎就是我在上面的 PasteBin sn-p 中所拥有的,也是答案所暗示的。应该绰绰有余。