【问题标题】:Computing hashcode of combination of value type and array计算值类型和数组组合的哈希码
【发布时间】:2021-11-07 00:25:07
【问题描述】:

我好像看不懂Hashcode struct的文档:

我尝试了一个天真的:

public struct S : IEquatable<S>
{
    public int I { get; set;  }
    public string[] A { get; set;  }

    public override bool Equals(object? obj) => obj is S s && Equals(s);

    public bool Equals(S other) => I == other.I && StructuralComparisons.StructuralEqualityComparer.Equals(A, other.A);

    // public override int GetHashCode() => I.GetHashCode() ^ StructuralComparisons.StructuralEqualityComparer.GetHashCode(A);
    public override int GetHashCode() => HashCode.Combine(I, A);
}

导致:

S s1 = new S() { I = 42, A = new string[] { "hello" } };
S s2 = new S() { I = 42, A = new string[] { "hello" } };
Assert.Equal(s1.GetHashCode(), s2.GetHashCode());

给出一个错误:

Xunit.Sdk.EqualException: 'Assert.Equal() Failure
Expected: 840823323
Actual:   -1160370390'

使用HashCode.Combine 的正确方法是什么(我不喜欢我之前的异或解决方案)?

【问题讨论】:

    标签: c# hashcode


    【解决方案1】:

    您正确地使用了HashCode.Combine。问题是您使用StructuralEqualityComparer 进行相等比较而不是生成哈希码,因此两个字符串数组“相等”但默认情况下不生成相同的哈希码。使用

    public override int GetHashCode() => HashCode.Combine(I, 
           StructuralComparisons.StructuralEqualityComparer.GetHashCode(A));
    

    这是必要的,因为Combine 将在每个实例上调用GetHashCode,无论列表中的值如何,列表都会有所不同。所以你需要指定如何你想生成哈希码。

    【讨论】:

    • 上游文档确实应该清楚地表明,人们可以传递以前的GetHashCode 调用的结果......否则这对我来说就像黑魔法。谢谢!
    • 本文档假定您知道引用类型的哈希码是如何工作的。默认情况下,无论“值”如何,每个实例的引用类型的哈希码都是不同的。它明确指出它结合了参数的哈希码,因此它将获得 list 的哈希码,无论值如何,它都是唯一的。为了保证相同的哈希码,您需要指定如何计算A 的哈希码。这个细节对于一般的哈希码是正确的,并不特定于HashCode.Combine
    • @malat 它起作用的唯一原因是int.GetHashCode 总是只返回自己。所以从技术上讲,这段代码得到一个散列码的散列码,而不是实际对象本身的散列码,但碰巧那些东西总是一样的因为如何int.GetHashCode 已实现。请注意,您可以使用HashCode.Add 来添加哈希值,并且它需要一个显式比较器,这是“技术上正确”的做法,但实际上并非必需 让它工作。
    • @Servy 那是我缺少的部分,非常感谢!
    猜你喜欢
    • 2014-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-21
    • 2011-02-25
    • 2020-07-24
    相关资源
    最近更新 更多