我正在将一个地理坐标类从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;意见和建议更受欢迎。 最佳答案
对我来说,经度/纬度的“位置”很好地落入了“不可变值”的槽中。位置本身不会改变-如果你改变纬度,那是一个不同的位置。从那里,它可能是一个struct
;对于float
,struct
无论如何都将与x64引用的大小相同,因此没有真正的下边。
是平等的;如果位置不完全相同,它就不是“平等的”,至少从“关键”的角度来看,所以我很高兴这里有==
。如果有帮助,您可以添加一个“is within(x)distance”方法。当然,伟大的弧线几何也不是完全自由的;p
尽管如此:
它应该覆盖bool Equals(object)
并添加一个bool Equals(GeoCoordinate)
它应该覆盖GetHashCode()
并实现IEquatable<GeoCoordinate>
静态运算符是一个很好的选择额外的