此测试失败:
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);
你能告诉我为什么吗?
答案 0 :(得分: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约束来比较每个字段并报告哪个字段不同。我在这里发布了一个约束:
答案 1 :(得分:1)
处理大量数据时是否遇到过这种情况?
欢迎来到哈希码的精彩世界。哈希码不是“唯一标识符”。它不可能。该匿名类型的可能的不同实例基本上是无限数量,但只有2 ^ 32个可能的哈希码。因此,如果您创建了足够多的这些对象,那么您将会看到一些重复的内容。实际上,如果你随机生成70,000个这些对象,那么其中两个具有相同哈希码的几率要好于50%。
有关详细信息,请参阅Birthdays, Random Numbers, and Hash Codes和链接的维基百科文章。
至于为什么有些人没有看到重复而其他人没有看到重复,他们可能会在不同版本的.NET上运行该程序。用于生成哈希码的算法不保证在不同版本或平台上保持相同:
对象的GetHashCode方法必须始终返回相同的值 哈希码只要没有对象状态的修改即可 确定对象的Equals方法的返回值。 请注意 这仅适用于当前执行的应用程序,并且 如果运行应用程序,则可以返回不同的哈希代码 再次强>
答案 2 :(得分:1)
您的测试无效。
由于哈希码不能保证唯一(请参阅其他答案以获得更好的解释),因此不应测试哈希码的唯一性。
在编写自己的GetHashCode()
方法时,最好测试随机输入的均匀分布,而不是唯一性。只需确保使用足够的随机输入即可获得良好的测试结果。
GetHashCode上的MSDN规范具体说明:
为获得最佳性能,哈希函数必须生成随机数 所有输入的分配。
当然,这都是相对的。用于将100个对象放入字典中的GetHashCode()
方法不需要像将{10,000}对象放入字典中的GetHashCode()
那样随机。
答案 3 :(得分: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种不同的组合进行测试,我就没有进行彻底检查。