Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何根据对象的内容为对象生成唯一的哈希代码?_C#_.net_Visual Studio 2010_.net 4.0_Hash - Fatal编程技术网

C# 如何根据对象的内容为对象生成唯一的哈希代码?

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编写缓存层 更新 我想我可能问错了问题。正

我需要根据对象的内容为其生成唯一的哈希代码,例如DateTime(2011,06,04)应该等于DateTime(2011,06,04)

  • 我无法使用.GetHashCode(),因为它可能会为具有不同内容的对象生成相同的哈希代码
  • 我无法使用ObjectedGenerator中的.GetID,因为它会为具有相同内容的对象生成不同的哈希代码
  • 如果对象包含其他子对象,则需要递归地检查这些子对象
  • 它需要处理集合
我为什么要写这个?我正在使用PostSharp编写缓存层

更新

我想我可能问错了问题。正如Jon Skeet所指出的,为了安全起见,我需要在缓存键中包含尽可能多的唯一组合,就像在对象中存在潜在数据的组合一样。因此,最好的解决方案可能是建立一个长字符串,使用反射对对象的公共属性进行编码。对象不太大,因此这是非常快速和高效的:

  • 构造缓存键(只需将对象的公共属性转换为一个大字符串)是非常有效的
  • 检查缓存命中(比较两个字符串)是有效的
如果您需要创建唯一的哈希代码,那么您基本上是在谈论一个数字,它可以表示您的类型可以具有的任意多个状态。对于
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();
    }