3

此测试失败:

var hashCode = new 
{
    CustomerId = 3354,
    ServiceId = 3,
    CmsThematicId = (int?)605,
    StartDate = (DateTime?)new DateTime(2013, 1, 5),
    EndDate = (DateTime?)new DateTime(2013, 1, 6)
}.GetHashCode();
var hashCode2 = new
{
    CustomerId = 1210,
    ServiceId = 3,
    CmsThematicId = (int?)591,
    StartDate = (DateTime?)new DateTime(2013, 3, 31),
    EndDate = (DateTime?)new DateTime(2013, 4, 1)
}.GetHashCode();
Assert.AreNotEqual(hashCode, hashCode2);

你能告诉我为什么吗 ?

4

4 回答 4

3

你发现这个巧合真是太神奇了。

匿名类有一个生成的GetHashCode()方法,它通过组合所有属性的哈希码来生成一个哈希码。

计算基本上是这样的:

  public override int GetHashCode()
  {
    return        -1521134295 * 
                ( -1521134295 * 
                ( -1521134295 * 
                ( -1521134295 * 
                ( -1521134295 * 
                   1170354300 + 
                  CustomerId.GetHashCode()) +
                  ServiceId.GetHashCode()) + 
                  CmsThematicId.GetHashCode()) + 
                  StartDate.GetHashCode()) + 
                  EndDate.GetHashCode();
  }

如果您更改任何字段的任何值,哈希码确实会更改。您发现两组不同的值恰好获得相同的哈希码,这是一个巧合。

请注意,哈希码不一定是唯一的。不可能说哈希码总是唯一的,因为对象可能比哈希码多(尽管对象很多)。好的散列码提供随机分布的值。

注意:以上来自 .NET 4。不同版本的 .NET 可能不同,Mono 也不同。

如果您想实际比较两个对象是否相等,请使用.Equals(). 对于匿名对象,它比较每个字段。一个更好的选择是使用 NUnit 约束来比较每个字段并报告哪个字段不同。我在这里发布了一个约束:

https://stackoverflow.com/a/2046566/118703

于 2013-01-29T14:31:58.337 回答
1

您在处理大量数据时遇到过这种情况吗?

欢迎来到哈希码的美妙世界。哈希码不是“唯一标识符”。不可能。该匿名类型的可能不同实例基本上是无限数量的,但只有 2^32 个可能的哈希码。所以可以保证,如果你创建了足够多的这些对象,你会看到一些重复的。事实上,如果你随机生成 70,000 个这样的对象,其中两个具有相同哈希码的几率超过 50%。

有关详细信息,请参阅生日、随机数和哈希码以及链接的 Wikipedia 文章。

至于为什么有些人没有看到重复而其他人看到,很可能他们在不同版本的 .NET 上运行该程序。不保证生成哈希码的算法在不同版本或平台之间保持相同:

只要确定对象的 Equals 方法的返回值的对象状态没有修改,对象的 GetHashCode 方法就必须始终返回相同的哈希码。请注意,这仅适用于应用程序的当前执行,如果再次运行应用程序,则可以返回不同的哈希码

于 2013-01-29T14:45:16.230 回答
1

您的测试无效。

因为哈希码不能保证是唯一的(请参阅其他答案以获得很好的解释),您不应该测试哈希码的唯一性。

在编写自己的GetHashCode()方法时,最好测试随机输入的均匀分布,而不是唯一性。只要确保您使用足够的随机输入来获得良好的测试。

GetHashCode 上的 MSDN 规范特别指出:

为了获得最佳性能,散列函数必须为所有输入生成随机分布。

当然,这都是相对的。用于将 100 个对象放入字典中的GetHashCode()方法不需要像GetHashCode()将 10,000,000 个对象放入字典中那样随机。

于 2013-01-29T15:22:22.897 回答
0

Jim 建议我(在聊天室)存储我的参数,所以当我显示我的参数时,选择未使用的参数,然后当有人注册时我将其标记为已使用。但是生成所有参数是一个很大的 PITA。

所以我的解决方案是像这样构建一个 int64 哈希码

const long i = -1521134295;    
return -i * (-i * (-i * (-i * -117147284 + customerId.GetHashCode()) + serviceId.GetHashCode()) + cmsThematicId.GetHashCode()) + startDate.GetHashCode();

我删除了结束日期,因为它的值取决于 serviceId 和 startDate 所以我不应该首先将它添加到哈希码中。我从生成的类的反编译中复制/粘贴它。如果我用 300 000 种不同的组合进行测试,我不会发生大肠杆菌。

于 2013-01-31T12:50:26.137 回答