ValueType GetHashCode/Equals 实现中嵌套小数行为的说明

Explanation for nested decimals behaviour in ValueType GetHashCode/Equals implementation

提问人:Oly 提问时间:9/14/2018 更新时间:9/14/2018 访问量:53

问:

我对自定义结构的两个实例之间的意外失败比较感到头疼。我希望有人能指出我参考来源或文档,或者确认这是否是一个错误!

提炼问题会得到如下结果:

public readonly struct TwoDecimals
{
    public readonly decimal First;
    public readonly decimal Second;
    public TwoDecimals(decimal first, decimal second)
    {
        this.First = first;
        this.Second = second;
    }
}

public void Main()
{
    var withoutTrailingZero = new TwoDecimals(42m, 42m);
    var withTrailingZero = new TwoDecimals(42.0m, 42m);
    var equal = withoutTrailingZero.Equals(withTrailingZero); // true
    var equalHashCodes = withoutTrailingZero.GetHashCode() == withTrailingZero.GetHashCode(); // false!
}

这是一个问题,因为如果为 true,则肯定应该返回相同的值。EqualsGetHashCode

我没有覆盖 or 中的任何一个,因此自定义结构应该获得默认实现,这可能是特定于平台的。但是在这里阅读参考来源,我看到有这样一种说法的评论:EqualsGetHashCodeValueType

我们返回哈希码的算法有点复杂。我们寻找第一个非静态字段并得到它的 [原文如此] 哈希码。

显然这不是真的,因为尽管具有不同的位,但具有相同的哈希码。42.0m42m

相反,似乎出于某种原因,它正在回退到使用这些位。不幸的是,这种方法是,我不确定如何找到/a 来源。extern

引用源确实具有 的实现,它使用反射来比较结构的每个字段。但首先,它执行此检查Equals

// if there are no GC references in this object we can avoid reflection 
// and do a fast memcmp
if (CanCompareBits(this))
    return FastEqualsCheck(thisObj, obj);

FastEqualsCheck,大概只是比较位。就我而言,它一定没有使用这种快速检查,因为位肯定不同,但我怀疑在实现中可能会发生类似的事情。GetHashCode


我还尝试了使用 s(并且具有不同的位但相同的哈希码并被认为是“相等”)和 s 的类似情况。行为仍然不如预期,但方式不同!这一次,哈希码再次不同(并且不像该代码注释中所建议的那样,等于第一个字段的哈希码)。但是这一次,该方法返回,因此至少它是一致的。double0.0d-0.0dfloatEqualsfalse

C# 相等 GetHashCode net-4.7.1

评论

1赞 Oly 9/14/2018
这个答案 stackoverflow.com/a/3842515/5181199 似乎部分解释了正在发生的事情,尽管它超出了给定问题的范围!
2赞 Jon Skeet 9/14/2018
这个答案似乎明确地回答了这个问题,尤其是最后一部分。鉴于这个答案 - 基本上它使用按位表示,这是一个错误,我不确定还有什么可以添加到这个问题的答案中。当然,这里的首选解决方法是自己覆盖 Equals/GetHashCode 并实现,但我确实很欣赏这种好奇心。IEquatable<TwoDecimals>
0赞 Matthew Watson 9/14/2018
另请参阅 stackoverflow.com/questions/5926776/...
0赞 Oly 9/15/2018
这个问题的答案似乎被隐藏在其他相关问题的答案中作为“额外的有趣事实”。我很想看到一些真实的文档,或者一个已知的错误说明。

答: 暂无答案