20

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

私はこれを実装しました:http: //alexmg.com/post/2009/04/16/Compute-any-hash-for-any-object-in-C.aspx

nHibernateを使用してデータベースからPOCOを取得しています。
これでGetHashを実行すると、データベースから選択されてハイドレイトされるたびに異なります。基になるプロキシが変更されるため、これは予想されることだと思います。

ともかく、

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

this.GetType()。GetProperties .....でStringBuilderを使用し、その上にハッシュを作成するというアイデアをいじくりまわしましたが、それは非効率的ですか?

補足として、これは、これらのエンティティを1つのデータベース(RDBMS)からNoSQLストアに変更追跡するためのものです(ハッシュ値を比較して、オブジェクトがrdbmsとnosqlの間で変更されたかどうかを確認します)

4

3 に答える 3

24

オーバーライドしない場合は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は、通常、データのブロックに基づいています。それが必要な場合は、各プロパティのハッシュコードを使用しても意味がありません。ヘンクが説明しているように、おそらくデータをメモリにシリアル化し、その方法でハッシュを計算する方が適切でしょう。

于 2012-09-13T03:48:10.800 に答える
11

この「ハッシュ」がエンティティが変更されたかどうかを判断するためだけに使用される場合は、次のアルゴリズムが役立つ可能性があります(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));
于 2012-09-12T18:02:53.327 に答える
-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をリセットしていました。

    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メソッド

同じプロパティ値を持つ2つのオブジェクト。それらは等しいですか?それらは同じ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();
            }
        }
于 2012-09-12T20:28:32.137 に答える