我有一个看起来像这样的课程:
public class NumericalRange:IEquatable<NumericalRange>
{
public double LowerLimit;
public double UpperLimit;
public NumericalRange(double lower, double upper)
{
LowerLimit = lower;
UpperLimit = upper;
}
public bool DoesLieInRange(double n)
{
if (LowerLimit <= n && n <= UpperLimit)
return true;
else
return false;
}
#region IEquatable<NumericalRange> Members
public bool Equals(NumericalRange other)
{
if (Double.IsNaN(this.LowerLimit)&& Double.IsNaN(other.LowerLimit))
{
if (Double.IsNaN(this.UpperLimit) && Double.IsNaN(other.UpperLimit))
{
return true;
}
}
if (this.LowerLimit == other.LowerLimit && this.UpperLimit == other.UpperLimit)
return true;
return false;
}
#endregion
}
此类包含一个数值范围的值。此类还应该能够保持默认范围,其中LowerLimit和UpperLimit都等于Double.NaN。
现在这个班级进入一个词典
“字典”适用于“非NaN”数值范围值,但当Key为{NaN,NaN} NumericalRange对象时,字典会抛出KeyNotFoundException。
我做错了什么?我还有其他任何接口要实现吗?
答案 0 :(得分:12)
根据您的评论,您尚未实施GetHashCode。我很惊讶这个类在字典中完全有效,除非你总是要求你输入的相同的键。我建议实现类似的东西:
public override int GetHashCode()
{
int hash = 17;
hash = hash * 23 + UpperLimit.GetHashCode();
hash = hash * 23 + LowerLimit.GetHashCode();
return hash;
}
假设 Double.GetHashCode()
为NaN提供一致的值。当然,NaN有很多值,你可能想要特殊情况,以确保它们都给出相同的哈希值。
您还应该覆盖从Equals
继承的Object
方法:
public override bool Equals(Object other)
{
return other != null &&
other.GetType() == GetType() &&
Equals((NumericalRange) other);
}
请注意,如果您密封课程,使用as
可以提高类型检查的效率。否则,如果有人从您的类中派生出另一个类,您将在x.Equals(y)
和y.Equals(x)
之间获得有趣的不对称。继承会使平等变得棘手。
你应 将你的字段设为私有,只将它们作为属性公开。如果这将被用作字典中的键,我强烈建议您也将它们作为只读。在字典中使用密钥时更改密钥的内容可能会导致密钥在以后“不可用”。
答案 1 :(得分:7)
GetHashCode方法的默认实现使用对象的引用而不是对象中的值。您必须使用与用于将数据放入字典中相同的对象实例才能使用该实例。
GetHashCode
的实现只是根据其数据成员的哈希码创建代码:
public int GetHashCode() {
return LowerLimit.GetHashCode() ^ UpperLimit.GetHashCode();
}
(这与Point结构使用的实现相同。)
对于任何给定参数值始终返回相同哈希码的方法的任何实现在Dictionary中使用时都有效。只返回所有值的相同哈希码实际上也可以工作,但随后字典的性能变差(查找键变为O(n)操作而不是O(1)操作。为了获得最佳性能,方法应该在范围内均匀分布哈希码。
如果您的数据存在偏差,则上述实施可能无法提供最佳效果。例如,如果您有很多范围,其中下限和上限相同,则它们都将获得哈希码零。在这种情况下,这样的事情可能会更好:
public int GetHashCode() {
return (LowerLimit.GetHashCode() * 251) ^ UpperLimit.GetHashCode();
}
您应该考虑使该类不可变,即将其属性设置为只读,并仅在构造函数中设置它们。如果您在字典中更改对象的属性,它的哈希代码将会更改,您将无法再访问该对象。