.net 4.0 为什么这些哈希码是相等的?

.net 4.0 为什么这些哈希码是相等的?,.net-4.0,gethashcode,.net 4.0,Gethashcode,此测试失败: 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 { Cu

此测试失败:

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);

你能告诉我为什么吗?

你发现这个巧合有点令人惊讶

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

计算基本上是这样的:

  public override int GetHashCode()
  {
    return        -1521134295 * 
                ( -1521134295 * 
                ( -1521134295 * 
                ( -1521134295 * 
                ( -1521134295 * 
                   1170354300 + 
                  CustomerId.GetHashCode()) +
                  ServiceId.GetHashCode()) + 
                  CmsThematicId.GetHashCode()) + 
                  StartDate.GetHashCode()) + 
                  EndDate.GetHashCode();
  }
如果更改任何字段的任何值,哈希代码也会更改。您发现两组不同的值恰好得到相同的哈希代码,这是一个巧合

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

注:以上内容来自.NET4。不同版本的.NET可能不同,Mono也可能不同

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


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

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

有关更多信息,请参阅和链接的Wikipedia文章

至于为什么有些人看不到副本,而有些人看到了,很可能是他们在不同版本的.NET上运行了这个程序。生成哈希代码的算法不能保证在不同版本或平台上保持不变:

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


你的测试无效

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

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

GetHashCode上的MSDN规范明确指出:

为了获得最佳性能,哈希函数必须生成一个随机变量 所有输入的分配

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

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

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

const long i = -1521134295;    
return -i * (-i * (-i * (-i * -117147284 + customerId.GetHashCode()) + serviceId.GetHashCode()) + cmsThematicId.GetHashCode()) + startDate.GetHashCode();
我删除了结束日期,因为它的值取决于serviceId和startDate,所以我不应该首先将其添加到hashcode中。
我从生成的类的反编译中复制/粘贴它。如果我用30万种不同的组合做测试,我就不会有结肠绞痛。

。他说得对。此代码确实生成相同的哈希代码。不要这么快就投票否决别人。@dasblinkenlight,运行代码。散列码是一样的。@SamuelNeff我刚刚做了-我上面的评论是到ideone的链接。我得到了429571660和-626441557..net4在标签中,如果我使用它,我会添加mono。随机分布是什么意思?随机分布不是一个准确的陈述。它应该说明的是随机输入的均匀分布。我会更新。我需要这些是唯一的,因为我想确保数据库中没有具有相同值组合的记录,您认为我应该如何做?我需要有效率,因为我要处理大量的数据。@remibourgarel:如果不了解更多关于数据的信息以及您如何使用这些数据,或者您希望这个独特的密钥做什么,就很难给出建议。一般来说,任何哈希方案都不能保证唯一性;总是有碰撞的危险。除非你说的是完美散列,这是一个非常特殊的情况。发布另一个问题,详细说明您正在尝试做什么,我们可能会提供解决方案。