2012-09-12 8 views
7

オブジェクトのハッシュ(md5またはsha)を取得しようとしています。オブジェクトのハッシュを一貫して生成する

私はこれを実装しました:私は、データベースからの私のPOCOSを取得するためにNHibernateはを使用してい http://alexmg.com/post/2009/04/16/Compute-any-hash-for-any-object-in-C.aspx


これでGetHashを実行すると、データベースから選択され水和されるたびに異なっています。私はこれが予想されていると思う、根本的な代理が変わるので。とにかく

は、オブジェクト、一貫たびにすべてのプロパティのハッシュを取得する方法はありますか?

私はthis.GetType()。GetProperties .....上でStringBuilderを使用するというアイデアを思いつき、その上にハッシュを作成しましたが、それは非効率的ですね。サイドノートとして

が、これはこの「ハッシュ場合

+0

セッション間にハッシュが保存されていますか? –

+1

これらのオブジェクトの直列化をシリアル化する方法の詳細。 GetHashCode()を上書きしていますか? – Paparazzi

答えて

13

あなたがGetHashCodeをオーバーライドしていない場合、あなただけObject.GetHashCodeを継承します。 Object.GetHashCodeは基本的にインスタンスのメモリアドレスを返すだけです(参照オブジェクトの場合)。もちろん、オブジェクトがロードされるたびに、メモリの異なる部分に読み込まれ、結果として異なるハッシュコードになる可能性があります。

これは正しいことかどうかは議論の余地があります。しかし、それは "今日に戻って"実装されたので、今変更することはできません。

何か一貫性を持たせたい場合は、GetHashCodeをオーバーライドし、オブジェクトの「値」(つまりプロパティやフィールド)に基づいてコードを作成する必要があります。これは、すべてのプロパティ/フィールドのハッシュコードを分散してマージするのと同じくらい単純なことができます。または、必要なだけ複雑になる可能性があります。 あなたが探しているものは、2つの異なるオブジェクトを区別するものであれば、そのオブジェクト上で一意のキーを使用すると効果があります。変更トラッキングを探している場合は、ハッシュのユニークキーを使用するとおそらく動作しません。

フィールドのすべてのハッシュコードを使用して、親オブジェクトの合理的に分散したハッシュコードを作成するだけです。例えば、:

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は、一般にデータブロックに基づいています。必要ならば、各プロパティのハッシュコードを使用することは意味がありません。 Henk氏が説明しているように、おそらくデータをメモリにシリアル化し、その方法でハッシュを計算する方がより適切でしょう。

+0

'Object.GetHashCode'は、インスタンスのメモリアドレスを返しません。これは、GC中に変更される可能性があるためです。実際には、最初のアクセスで生成され、オブジェクトヘッダーがポイントする乱数だけです。詳細については、SyncBlockIndexを参照してください.SyncBlockIndexは、ハッシュコードとモニターのために使用されます。 – andrewjs

6

(オブジェクトがRDBMSとNoSQLの間で変更かどうかを確認するためのハッシュ値を比較)のNoSQLストア 1つのデータベース(RDBMS)からこれらのエンティティを変更は、追跡するためのものです'は、エンティティが変更されたかどうかを判断するためにのみ使用され、以下のアルゴリズムが役立つ可能性があります(NBはテストされず、ハッシュを生成するときに同じランタイムが使用されることを前提としています)。

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ではなく)を返します。

同じプロパティ値を持つ2つのオブジェクトを作成した場合、ベースまたはシステムのGetHashCode()を使用する場合、同じハッシュはありません。

文字列はオブジェクトであり例外です。

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

2つのオブジェクトの等価比較を制御する場合は、GetHashとEqualityをオーバーライドする必要があります。

2つのオブジェクトが同じ場合、それらは同じGetHash()も持つ必要があります。しかし、同じGetHash()を持つ2つのオブジェクトは必ずしも同じではありません。比較はまずGetHash()をテストし、そこに一致するものがあればEqualsをテストします。 OKはいくつかの比較がEqualsにまっすぐ行くが、あなたはまだ両方をオーバーライドし、2つの同一のオブジェクトが同じGetHashを生成していることを確認する必要があります。

私はこれを使用してクライアントをサーバーと同期させます。すべてのプロパティを使用することもできますし、プロパティの変更によりVerIDを変更することもできます。ここでの利点は、より簡単なGetHashCode()です。私の場合は、すでにプロパティの変更を行ってVerIDをリセットしていました。

​​

私は同じプロパティ値を次のよう

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

Object.GetHashCode Method

2つのオブジェクトを行うことができますので、私は一人のObjID使用して終了。彼らは等しいのですか?彼らは同じ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への呼び出し回数を減らすための安価な方法です。 – Paparazzi

+0

@exacerbatedexpertしかし、deserializeの場合、システムGetHashを使用し、同じプロパティを持つオブジェクトはランダムなGetHashのために同じMD5を持たないのはどうですか? – Paparazzi

+0

@exacerbatedexpert質問:「これでGetHashを実行すると、データベースから選択され、水和されるたびに異なる」という質問を参照してください。 OKシリアル化はGetHashCodeを直接使用することはできませんが、Objectはそれを行います。 – Paparazzi