2012-09-12 27 views
7

我试图得到一个对象的哈希(md5或sha)。一致地生成对象的哈希值

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

我使用NHibernate从数据库中检索我的波苏斯。
当在此上运行GetHash时,每次从数据库中选择并水化时都不同。我猜这是预期的,因为底层代理将会改变。

反正

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

我玩弄了使用StringBuilder over this.GetType()。GetProperties .....并创建一个散列,但似乎效率低下的想法?

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

+0

会话之间存储哈希值吗? –

+1

有关如何序列化反序列化这些对象的更多信息。你是否覆盖GetHashCode()? – Paparazzi

回答

13

如果你不重写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通常基于一个数据块。如果你想要的话,那么使用每个属性的哈希码是没有意义的。正如亨克所描述的那样,将数据序列化到内存并计算哈希将更加适用。

+0

'Object.GetHashCode'不返回实例的内存地址,因为在GC期间这可能会改变。它实际上只是第一次访问时生成的一个随机数,由对象头指向。有关更多信息,请阅读SyncBlockIndex,其中包括用于哈希代码和监视器的内容。 – andrewjs

6

如果这个“散列'仅用于确定实体是否发生了变化,那么以下算法可能会有所帮助(注意它未经测试,并假定在生成散列时将使用相同的运行时(否则,对'简单'类型的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)); 
-1

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

如果您使用所有相同的属性值创建两个对象,那么如果使用基本或系统GetHashCode(),它们将不会具有相同的散列。

字符串是一个对象和一个异常。

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

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

如果两个对象相同,那么它们也必须具有相同的GetHash()。但是具有相同GetHash()的两个对象不一定是相同的。比较将首先测试GetHash(),如果它得到一个匹配,那么它将测试Equals。好的,有一些比较直接进入Equals,但你仍然应该覆盖它们,并确保两个相同的对象产生相同的GetHash。

我用它来同步客户端与服务器。您可以使用所有属性,也可以使用任何Property更改VerID。这里的优点是更简单快速的GetHashCode()。在我的情况下,我已经重置了任何Property更改VerID。

​​

我结束了使用的ObjID孤单,所以我可以做相同的属性值以下

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

Object.GetHashCode Method

两个对象。他们是平等的吗?他们是否产生相同的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(); 
      } 
     } 
+0

@exacerbatedexpert但是,这正是关键。任何更改不一定是新版本。 Serialize/deserialzie可以引入实际上未被更改的对象的MD5中的更改。如果我明天穿不同的衬衫,我是不是另一个人?更独特的是不重要的。 GetHashCode本身并不决定唯一性。等于决定唯一性。 GetHashCode的目的是一种廉价的方法,可以减少更昂贵的Equals的调用次数。它是HashSet和字典的基础 – Paparazzi

+0

@exacerbatedexpert但是,反序列化的情况如何使用系统GetHash和具有相同属性的对象由于随机GetHash而不具有相同的MD5? – Paparazzi

+0

@exacerbatedexpert请参阅问题“在此上运行GetHash时,每次从数据库中选择并水合时都会有所不同。” OK序列化可能不直接使用GetHashCode,而是Object。 – Paparazzi