C# 如何根据对象的内容为对象生成唯一的哈希代码?
我需要根据对象的内容为其生成唯一的哈希代码,例如DateTime(2011,06,04)应该等于DateTime(2011,06,04)C# 如何根据对象的内容为对象生成唯一的哈希代码?,c#,.net,visual-studio-2010,.net-4.0,hash,C#,.net,Visual Studio 2010,.net 4.0,Hash,我需要根据对象的内容为其生成唯一的哈希代码,例如DateTime(2011,06,04)应该等于DateTime(2011,06,04) 我无法使用.GetHashCode(),因为它可能会为具有不同内容的对象生成相同的哈希代码 我无法使用ObjectedGenerator中的.GetID,因为它会为具有相同内容的对象生成不同的哈希代码 如果对象包含其他子对象,则需要递归地检查这些子对象 它需要处理集合 我为什么要写这个?我正在使用PostSharp编写缓存层 更新 我想我可能问错了问题。正
- 我无法使用.GetHashCode(),因为它可能会为具有不同内容的对象生成相同的哈希代码
- 我无法使用ObjectedGenerator中的.GetID,因为它会为具有相同内容的对象生成不同的哈希代码
- 如果对象包含其他子对象,则需要递归地检查这些子对象
- 它需要处理集合
- 构造缓存键(只需将对象的公共属性转换为一个大字符串)是非常有效的
- 检查缓存命中(比较两个字符串)是有效的
DateTime
而言,我相信这意味着采用Ticks值和DateTimeKind
您可以假设Ticks
属性的前两位为零,并使用它们来存储该类数据。就我所知,这意味着在7307年之前你都很好:
private static ulong Hash(DateTime when)
{
ulong kind = (ulong) (int) when.Kind;
return (kind << 62) | (ulong) when.Ticks;
}
private静态ulong散列(DateTime-when)
{
ulong kind=(ulong)(int)when.kind;
回报(实物)
我无法使用.GetHashCode(),因为它可能会为具有不同内容的对象生成相同的哈希代码
哈希代码发生冲突是很正常的。如果您的哈希代码具有固定长度(标准.NET哈希代码为32位),则您必然会与范围大于此值的任何值发生冲突(例如,长为64位;长为n的数组为n*64位等)
事实上,对于任何长度为N的哈希代码,对于超过N个元素的集合,总是会发生冲突
你所要求的在一般情况下是不可行的。你在这里不是在谈论散列码,你需要一个状态的数字表示——要使其唯一,它可能必须非常大,这取决于你的对象结构
我写这篇文章的原因是什么?我
使用
波斯夏普
你为什么不使用一个常规的Hash码,并通过实际比较对象来处理冲突?这似乎是最合理的方法。
< P> > BrkungLas'的答案,我已经投票并认为是正确的:
使用GetHashCode
/Equals
方法意味着,如果两个对象散列为相同的值,您将依靠它们的Equals
实现来告诉您它们是否相等
除非这些对象覆盖Equals
(这实际上意味着它们实现IEquatable
,其中T
是它们的类型),否则Equals
的默认实现将进行引用比较。这反过来意味着您的缓存将错误地产生“equal”对象的未命中在商业意义上,但已独立构建
仔细考虑缓存的使用模型,因为如果您最终将其用于不可IEquatable
的类,并且以您希望检查非引用相等对象是否相等的方式使用,缓存将变得完全无用。此扩展方法是否适合您的目的对象是一种值类型,它只返回其哈希代码。否则,它递归地获取每个属性的值,并将它们组合成一个哈希
using System.Reflection;
public static class HashCode
{
public static ulong CreateHashCode(this object obj)
{
ulong hash = 0;
Type objType = obj.GetType();
if (objType.IsValueType || obj is string)
{
unchecked
{
hash = (uint)obj.GetHashCode() * 397;
}
return hash;
}
unchecked
{
foreach (PropertyInfo property in obj.GetType().GetProperties())
{
object value = property.GetValue(obj, null);
hash ^= value.CreateHashCode();
}
}
return hash;
}
}
在评论中:
我想要一个基于对象内容的GUID。我不介意每隔10万亿年左右偶尔会有重复
这似乎是一个不寻常的要求,但既然这是你的要求,让我们来计算一下
假设你在10万亿年的时间里每年制造10亿个独特对象,每秒30个。这就是你正在创建的1049个独特对象。算出数学很容易;当哈希的位大小小于384时,在这段时间内至少发生一次哈希冲突的概率高于1018分之一。
因此,您需要至少384位哈希代码才能达到所需的唯一性级别。这是一个方便的大小,为12 Int32。如果您要每秒生成30个以上的对象,或者希望概率小于1018分之一,则需要更多位
你为什么有如此严格的要求
如果我有你明确的要求,我会这么做。第一个问题是将每个可能的数据转换成一个自描述的位序列。如果你已经有了序列化格式,就使用它。如果没有,就发明一种可以序列化你感兴趣的所有可能对象的格式
然后,要散列对象,将其序列化为字节数组,然后通过SHA-384或SHA-512散列算法运行字节数组。这将产生一个专业的加密级别384或512位散列,即使在攻击者试图强制冲突的情况下,该散列也被认为是唯一的。许多位应该足够多,以确保低成本在你们10万亿年的时间范围内发生碰撞的概率。我们有完全相同的要求
public static string CreateCacheKey(this object obj, string propName = null)
{
var sb = new StringBuilder();
if (obj.GetType().IsValueType || obj is string)
sb.AppendFormat("{0}_{1}|", propName, obj);
else
foreach (var prop in obj.GetType().GetProperties())
{
if (typeof(IEnumerable<object>).IsAssignableFrom(prop.PropertyType))
{
var get = prop.GetGetMethod();
if (!get.IsStatic && get.GetParameters().Length == 0)
{
var collection = (IEnumerable<object>)get.Invoke(obj, null);
if (collection != null)
foreach (var o in collection)
sb.Append(o.CreateCacheKey(prop.Name));
}
}
else
sb.AppendFormat("{0}{1}_{2}|", propName, prop.Name, prop.GetValue(obj, null));
}
return sb.ToString();
}
var bar = new Bar()
{
PropString = "test string",
PropInt = 9,
PropBool = true,
PropListString = new List<string>() {"list string 1", "list string 2"},
PropListFoo =
new List<Foo>()
{new Foo() {PropString = "foo 1 string"}, new Foo() {PropString = "foo 2 string"}},
PropListTuple =
new List<Tuple<string, int>>()
{
new Tuple<string, int>("tuple 1 string", 1), new Tuple<string, int>("tuple 2 string", 2)
}
};
var cacheKey = bar.CreateCacheKey();
public static string GetChecksum(this YourClass obj)
{
var copy = new
{
obj.Prop1,
obj.Prop2
};
var json = JsonConvert.SerializeObject(ob);
return json.CalculateMD5Hash();
}