C#:如何对GetHashCode进行单元测试?

C#:如何对GetHashCode进行单元测试?,c#,unit-testing,tdd,gethashcode,C#,Unit Testing,Tdd,Gethashcode,测试Equals方法非常简单(据我所知)。但是你究竟如何测试GetHashCode方法呢?我会预先提供一个已知/预期的散列,并比较GetHashCode的结果。测试两个相等的不同对象是否具有相同的散列码(对于不同的值)。检查非相等对象是否提供不同的哈希代码,每次改变一个方面/属性。虽然散列代码不必不同,但如果为恰好给出相同散列代码的属性选择不同的值,那将是非常不幸运的,除非您有错误。这与Equals()非常相似。您需要确保两个“相同”的对象至少具有相同的哈希代码。这意味着如果.Equals()返

测试
Equals
方法非常简单(据我所知)。但是你究竟如何测试
GetHashCode
方法呢?

我会预先提供一个已知/预期的散列,并比较GetHashCode的结果。

测试两个相等的不同对象是否具有相同的散列码(对于不同的值)。检查非相等对象是否提供不同的哈希代码,每次改变一个方面/属性。虽然散列代码不必不同,但如果为恰好给出相同散列代码的属性选择不同的值,那将是非常不幸运的,除非您有错误。

这与Equals()非常相似。您需要确保两个“相同”的对象至少具有相同的哈希代码。这意味着如果.Equals()返回true,则哈希代码也应该相同。至于正确的hashcode值是什么,这取决于散列的方式。

使用相同的值创建单独的实例,并检查实例的GetHashCode是否返回相同的值,以及对相同实例的重复调用是否返回相同的值


这是哈希代码工作的唯一要求。当然,要使散列码正常工作,散列码应该有一个良好的分布,但是测试散列码需要大量的测试…

根据个人经验。除了像相同对象提供相同哈希代码这样明显的事情之外,您还需要创建足够大的唯一对象数组,并计算其中的唯一哈希代码。如果唯一的散列代码生成的值小于,比如说,对象总数的50%,那么您就有麻烦了,因为您的散列函数不好

        List<int> hashList = new List<int>(testObjectList.Count);
        for (int i = 0; i < testObjectList.Count; i++)
        {
            hashList.Add(testObjectList[i]);
        }

        hashList.Sort();
        int differentValues = 0;
        int curValue = hashList[0];
        for (int i = 1; i < hashList.Count; i++)
        {
            if (hashList[i] != curValue)
            {
                differentValues++;
                curValue = hashList[i];
            }
        }

        Assert.Greater(differentValues, hashList.Count/2);
List hashList=新列表(testObjectList.Count);
for(int i=0;i
附带了方便的合同验证程序,可以测试
GetHashCode()
IEquatable
的实现。更具体地说,您可能对
EqualityContract
HashCodeAcceptanceContract
感兴趣。有关详细信息,请参见和

public class Spot
{
  private readonly int x;
  private readonly int y;

  public Spot(int x, int y)
  {
    this.x = x;
    this.y = y;
  }

  public override int GetHashCode()
  {
    int h = -2128831035;
    h = (h * 16777619) ^ x;
    h = (h * 16777619) ^ y;
    return h;
  }
}
然后你像这样声明你的合同验证人:

[TestFixture]
public class SpotTest
{
  [VerifyContract]
  public readonly IContract HashCodeAcceptanceTests = new HashCodeAcceptanceContract<Spot>()
  {
    CollisionProbabilityLimit = CollisionProbability.VeryLow,
    UniformDistributionQuality = UniformDistributionQuality.Excellent,
    DistinctInstances = DataGenerators.Join(Enumerable.Range(0, 1000), Enumerable.Range(0, 1000)).Select(o => new Spot(o.First, o.Second))
  };
}
[TestFixture]
公开课点检
{
[核实合同]
公共只读IContract HashCodeAcceptanceTests=新HashCodeAcceptanceContract()
{
CollisionProbabilityLimit=CollisionProbability.VeryLow,
UniformDistributionQuality=UniformDistributionQuality.非常好,
differentinstances=DataGenerators.Join(可枚举的范围(0,1000),可枚举的范围(0,1000))。选择(o=>newspot(o.First,o.Second))
};
}

除了检查对象的相等性意味着哈希码的相等性外,散列的分布也相当于Yann Trevin所建议的(如果性能是一个问题),你也可以考虑如果改变对象的属性会发生什么。


假设对象在字典/哈希集中时发生更改。是否希望Contains(对象)仍然为true?如果是这样,那么GetHashCode最好不要依赖于已更改的可变属性。

这使得测试非常脆弱。例如,您应该能够使GetHashCode返回它在以前版本中给出的否定值,并且该方法仍然有效。测试您关心的内容-即比较相等值和非相等值的哈希代码。仅仅因为其他人的代码无法生成分布良好的哈希值,并不意味着这不是对您的代码的好测试。@Tony:那么您通常使用什么?@Svish:我记不起名称,但是重复的乘法和加法-我会找到关于GetHashCode的答案,我相信你会看到很多例子:)据我所知,单元测试的目的是验证方法是否按照文档中的方式工作。GetHashCode的唯一要求是:
如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。
为不同的对象返回不同的值是一个性能问题:GetHashCode始终返回0是一个严重的性能缺陷,但实际上它仍然是一个有效的哈希函数。这应该是某种代码审查的主题,而不是单元测试。我说的对吗?我想说的是,如果GetHashCode为相等的对象返回相同的值,那么只需要执行一个测试检查,GetHashCode被用户覆盖并实现。+1-这绝对是需要测试的一件事。忘记分发,但相同的对象必须具有相同的哈希代码。