始终生成对象的哈希值

Ale*_*lex 12 .net c# hash

我正在尝试获取对象的哈希值(md5或sha).

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

我正在使用nHibernate从数据库中检索我的POCO.
在此运行GetHash时,每次从数据库中选择并保湿时都会有所不同.我想这是预期的,因为底层代理会改变.

无论如何,

有没有办法在对象上获取所有属性的哈希值,每次都是一致的?

我已经玩弄了使用StringBuilder而不是this.GetType().GetProperties .....并在其上创建哈希,但这似乎效率低下的想法?

作为旁注,这是用于将这些实体从一个数据库(RDBMS)更改跟踪到NoSQL存储(比较哈希值以查看rdbms和nosql之间是否更改了对象)

Pet*_*hie 17

如果你没有压倒GetHashCode你只是继承Object.GetHashCode. Object.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

素数397的使用是为值生成唯一的数字以更好地分配哈希码.有关在哈希码计算中使用素数的更多详细信息,请参见http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/.

当然,您可以使用反射来获取所有属性来执行此操作,但这会更慢.或者,您可以使用CodeDOM动态生成代码,以基于对属性的反映生成散列并缓存该代码(即生成一次并在下次重新加载它).但是,这当然是非常复杂的,可能不值得努力.

MD5或SHA哈希或CRC通常基于数据块.如果你想要,那么使用每个属性的哈希码是没有意义的.正如Henk所描述的那样,可能将数据序列化到内存并以这种方式计算哈希将更加适用.


Ric*_*lly 7

如果此'hash'仅用于确定实体是否已更改,则以下算法可能有所帮助(注意它未经测试并假设在生成哈希时将使用相同的运行时(否则对'简单'类型的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);
  }
} 
Run Code Online (Sandbox Code Playgroud)

这样就可以这样使用:

var entity1 = LoadEntityFromRdbms();
var entity2 = LoadEntityFromNoSql();
var hash1 = Hash(entity1);
var hash2 = Hash(entity2);
Assert.IsTrue(hash1.SequenceEqual(hash2));
Run Code Online (Sandbox Code Playgroud)