2012-09-12 53 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