生成对象的哈希值一致(Generate hash of object consistently)

2019-07-31 13:07发布

我试图得到一个对象的哈希值(MD5或SHA)。

我实现了这一点: http://alexmg.com/post/2009/04/16/Compute-any-hash-for-any-object-in-C.aspx

我使用NHibernate从数据库中检索我的波苏斯。
当这个运行GetHash,它选中它,并从数据库水合每次都不同了。 我想这是意料之中的,因为底层的代理会发生变化。

无论如何,

有没有办法让所有属性的哈希值的对象,每次持续时间?

我玩弄使用一个StringBuilder在this.GetType()。GetProperties中.....并创建对散列的想法,但似乎效率不高?

作为一个方面说明,这是一个变化跟踪这些实体从一个数据库(RDBMS)的NoSQL的存储(比较哈希值,看看是否对象RDBMS和NoSQL之间变化)

Answer 1:

如果你不重写GetHashCode你只是继承Object.GetHashCodeObject.GetHashCode基本上只是返回实例的内存地址,如果它是一个参考对象。 当然,每个对象被加载时它可能会被加载到存储器的不同部分,从而导致不同的哈希码。

这是值得商榷不管是做正确的事; 但是这已经实现了什么“早在天”,所以现在不能改变。

如果你想要的东西一致的,那么你必须重写GetHashCode和创建基于对象的“价值”(即性能和/或领域)代码。 这可以像所有属性/字段的散列码的分布式合并一样简单。 或者,当你需要它也可能是一样复杂。 如果你正在寻找的东西来区分两个不同的对象,然后使用对象上的唯一密钥可能为你工作。 如果你正在寻找的变化跟踪,使用哈希的唯一钥匙可能是行不通的

我只是使用的字段的所有散列码创建对父对象的合理分配哈希码。 例如:

public override int GetHashCode()
{
    unchecked
    {
        int result = (Name != null ? Name.GetHashCode() : 0);
        result = (result*397) ^ (Street != null ? Street.GetHashCode() : 0);
        result = (result*397) ^ Age;
        return result;
    }
}

使用该素数397是生成一个唯一的编号为价值,以更好地分配哈希码。 见http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/用于在散列码的计算中使用的素数的更多细节。

你可以,当然,使用反射来获取所有属性要做到这一点,但是这将是慢。 另外,您可以使用CodeDOM的动态生成的代码来生成基于反映属性的散列和缓存的代码(即一旦产生,并在下一次重新加载它)。 但是,这当然是非常复杂的,可能不值得努力。

的MD5或SHA散列或CRC通常是基于数据块上。 如果你想要的,然后利用每个属性的哈希码是没有意义的。 该数据可能序列化到存储和计算散列这种方式会比较适用,亨克描述。



Answer 2:

如果这个“散列”仅用于确定实体是否已经改变,那么下面的算法可以帮助(NB这是未经测试,假设同一个运行时将用于生成散列时(否则上的GetHashCode的依赖于“简单”的类型是不正确)):

public static byte[] Hash<T>(T entity) 
{
  var seen = new HashSet<object>();
  var properties = GetAllSimpleProperties(entity, seen);
  return properties.Select(p => BitConverter.GetBytes(p.GetHashCode()).AsEnumerable()).Aggregate((ag, next) => ag.Concat(next)).ToArray();
}

private static IEnumerable<object> GetAllSimpleProperties<T>(T entity, HashSet<object> seen)
{
  foreach (var property in PropertiesOf<T>.All(entity))
  {
    if (property is int || property is long || property is string ...) yield return property;
    else if (seen.Add(property)) // Handle cyclic references
    {
      foreach (var simple in GetAllSimpleProperties(property, seen)) yield return simple;
    }
  }
}

private static class PropertiesOf<T>
{
  private static readonly List<Func<T, dynamic>> Properties = new List<Func<T, dynamic>>();

  static PropertiesOf()
  {
    foreach (var property in typeof(T).GetProperties())
    {
      var getMethod = property.GetGetMethod();
      var function = (Func<T, dynamic>)Delegate.CreateDelegate(typeof(Func<T, dynamic>), getMethod);
      Properties.Add(function);
    }
  }

  public static IEnumerable<dynamic> All(T entity) 
  {
    return Properties.Select(p => p(entity)).Where(v => v != null);
  }
} 

那么这将是可用的,如下所示:

var entity1 = LoadEntityFromRdbms();
var entity2 = LoadEntityFromNoSql();
var hash1 = Hash(entity1);
var hash2 = Hash(entity2);
Assert.IsTrue(hash1.SequenceEqual(hash2));


Answer 3:

GetHashCode()方法返回一个Int32(不是MD5)。

如果您创建了所有相同属性值的两个对象,他们不会有相同的哈希如果使用底座或系统的GetHashCode()。

字符串是一个对象,一个例外。

string s1 = "john";
string s2 = "john";
if (s1 == s2) returns true and will return the same GetHashCode()

如果你想控制的两个对象的相等比较,那么你应该重写GetHash和平等。

如果两个对象相同,则他们也必须有相同的GetHash()。 但与同GetHash()两个对象不一定是相同的。 比较会先测试GetHash(),如果它获得匹配的还有它将测试等于。 OK也有一些比较是直接去的Equals,但你还是应该会覆盖并确保两个相同的物体产生相同的GetHash。

我用这个与服务器同步的客户端。 你可以使用所有的属性,或者你可以拥有任何产权变化而变化的VERID。 这样做的优点是简单,快捷的GetHashCode()。 以我为例,我重置VERID任何属性更改了。

    public override bool Equals(Object obj)
    {
        //Check for null and compare run-time types.
        if (obj == null || !(obj is FTSdocWord)) return false;
        FTSdocWord item = (FTSdocWord)obj;
        return (OjbID == item.ObjID && VerID == item.VerID);
    }
    public override int GetHashCode()
    {
        return ObjID ^ VerID;
    }

最后我用的ObjID孤单,所以我可以做以下

if (myClientObj == myServerObj && myClientObj.VerID <> myServerObj.VerID)
{
   // need to synch
}

Object.GetHashCode方法

两个对象具有相同的属性值。 他们是平等的吗? 难道他们产生相同的GetHashCode()?

            personDefault pd1 = new personDefault("John");
            personDefault pd2 = new personDefault("John");
            System.Diagnostics.Debug.WriteLine(po1.GetHashCode().ToString());
            System.Diagnostics.Debug.WriteLine(po2.GetHashCode().ToString()); 
            // different GetHashCode
            if (pd1.Equals(pd2))  // returns false
            {
                System.Diagnostics.Debug.WriteLine("pd1 == pd2");
            }
            List<personDefault> personsDefault = new List<personDefault>();
            personsDefault.Add(pd1);
            if (personsDefault.Contains(pd2))  // returns false
            {
                System.Diagnostics.Debug.WriteLine("Contains(pd2)");
            }

            personOverRide po1 = new personOverRide("John");
            personOverRide po2 = new personOverRide("John");
            System.Diagnostics.Debug.WriteLine(po1.GetHashCode().ToString());
            System.Diagnostics.Debug.WriteLine(po2.GetHashCode().ToString());  
            // same hash
            if (po1.Equals(po2))  // returns true
            {
                System.Diagnostics.Debug.WriteLine("po1 == po2");
            }
            List<personOverRide> personsOverRide = new List<personOverRide>();
            personsOverRide.Add(po1);
            if (personsOverRide.Contains(po2))  // returns true
            {
                System.Diagnostics.Debug.WriteLine("Contains(p02)");
            }
        }



        public class personDefault
        {
            public string Name { get; private set; }
            public personDefault(string name) { Name = name; }
        }

        public class personOverRide: Object
        {
            public string Name { get; private set; }
            public personOverRide(string name) { Name = name; }

            public override bool Equals(Object obj)
            {
                //Check for null and compare run-time types.
                if (obj == null || !(obj is personOverRide)) return false;
                personOverRide item = (personOverRide)obj;
                return (Name == item.Name);
            }
            public override int GetHashCode()
            {
                return Name.GetHashCode();
            }
        }


文章来源: Generate hash of object consistently
标签: c# .net hash