始终生成对象的哈希值

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

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

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

无论如何,

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

我已经玩弄了在这个.GetType()。GetProperties …..上使用StringBuilder并在其上创建哈希的想法,但这看起来效率低下?

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

如果您没有覆盖GetHashCode ,则只需inheritanceObject.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/ 。

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

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

如果此’hash’仅用于确定实体是否已更改,则以下算法可能有所帮助(注意它未经测试并假设在生成哈希时将使用相同的运行时(否则对’简单’类型的GetHashCode的依赖不正确)):

 public static byte[] Hash(T entity) { var seen = new HashSet(); var properties = GetAllSimpleProperties(entity, seen); return properties.Select(p => BitConverter.GetBytes(p.GetHashCode()).AsEnumerable()).Aggregate((ag, next) => ag.Concat(next)).ToArray(); } private static IEnumerable GetAllSimpleProperties(T entity, HashSet seen) { foreach (var property in PropertiesOf.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 { private static readonly List> Properties = new List>(); static PropertiesOf() { foreach (var property in typeof(T).GetProperties()) { var getMethod = property.GetGetMethod(); var function = (Func)Delegate.CreateDelegate(typeof(Func), getMethod); Properties.Add(function); } } public static IEnumerable 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)); 

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

如果使用所有相同的属性值创建两个对象,则在使用基本或系统GetHashCode()时,它们将不具有相同的哈希值。

String是一个对象和一个例外。

 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。

我用它来同步客户端和服务器。 您可以使用所有属性,也可以更改任何属性更改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方法

两个具有相同属性值的对象。 他们是平等的吗? 他们生成相同的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 personsDefault = new List(); 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 personsOverRide = new List(); 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(); } }