我正在尝试创建一个哈希码方法。我有如下代码:
private static object GetValue<T>(object item, string propertyName)
{
ParameterExpression arg = Expression.Parameter(item.GetType(), "x");
Expression expr = Expression.Property(arg, propertyName);
UnaryExpression unaryExpression = Expression.Convert(expr, typeof(object));
var propertyResolver = Expression.Lambda<Func<T, object>>(unaryExpression, arg).Compile();
return propertyResolver((T)item);
}
private static int GetHashCode<T>(T obj, List<string> columns)
{
unchecked
{
int hashCode = 17;
for (var i = 0; i < columns.Count; i++)
{
object value = GetValue<T>(obj, columns[i]);
var tempHashCode = value == null ? 0 : value.GetHashCode();
hashCode = (hashCode * 23) + tempHashCode;
}
return hashCode;
}
}
private static void TestHashCode()
{
var t1 = new { ID = (long)2044716, Type = "AE", Method = (short)1022, Index = 3 };
var t2 = new { ID = (long)12114825, Type = "MEDAPE", Method = (short)1700, Index = 2 };
var e1 = t1.GetHashCode();
var e2 = t2.GetHashCode();
var columns = new[] { "ID", "Type", "Method", "Index" }.ToList();
var k1 = GetHashCode(t1, columns);
var k2 = GetHashCode(t2, columns);
}
e1值为-410666035,e2值为101205027。 k1值为491329214。k2值为491329214。
HashCode步骤:
hashCode = 17
tempHashCode = 2044716
哈希码= 2045107
tempHashCode = 1591023428
哈希码= 1638060889
tempHashCode = 66978814
哈希码= -912326403
tempHashCode = 3
哈希码= 491329214
k1和k2为何值相同?因为默认的.net gethashcode方法提供了两个不同的值。我想创建一个可以获取列列表的哈希码方法。我想通过特定属性创建一个哈希码。我正在尝试通过特定属性为对象获取唯一值。
如果GetHashCode不保证唯一值,如何通过特定属性标识对象?
答案 0 :(得分:0)
我怀疑问题是由您的value.GetHashCode()
方法中的GetHashCode<T>
引起的。该值变量在那里是一个对象,我认为GetHashCode()
不会返回您期望的结果。尝试调试以找出正在发生的情况。
您可能想尝试保留代码,但请使用Object.GetHashCode()
(来自名称空间RuntimeHelpers.GetHashCode()
)而不是System.Runtime.CompilerServices
。
祝你好运!
答案 1 :(得分:0)
GetHashCode
返回取决于实现的值。其特殊的设计适合“标准”使用,并且仅在应用程序的生命周期内才有意义。默认算法并非旨在避免冲突。
GetHashCode
方法并非针对每个实例都是唯一的。
您的方法依赖于每列哈希的组成。哈希码必须满足某些要求,例如域中的分布。但是,不能保证该组合物保留此类属性和要求:添加的列越多,冲突可能就越“陌生”。
此外,您正在调用value.GetHashCode()
,这阻碍了装箱操作。正如johey所建议的那样,您应该使用RuntimeHelpers.GetHashCode()
方法,因为它在计算哈希值之前会将对象解释为值。
.NET数据结构旨在内部处理冲突,例如,IDictionary
使用散列来选择存储桶,然后依次扫描该存储桶。
答案 2 :(得分:0)
我想在这里写下我的解决方案。所有的说法都是正确的,但并非完全正确。我想在这里收集话题。
GetHashCode
始终为相同的对象提供相同的值。
GetHashCode
的值可能始终不属于不同的对象。
因此,首先比较GetHashCode
的值以提高性能,然后如果GetHashCode
的值相同,则下一步进行对象比较。
我创建了IEqualityComparer。
private class CustomEqualityComparer<T> : IEqualityComparer<T>
{
private readonly List<string> _columns;
private readonly bool _enableHashCode;
private readonly ConcurrentDictionary<string, Func<T, object>> _cache;
public CustomEqualityComparer(List<string> columns, ConcurrentDictionary<string, Func<T, object>> cache, bool enableHashCode = false)
{
_columns = columns;
_enableHashCode = enableHashCode;
_cache = cache;
}
public bool Equals(T x, T y)
{
for (var i = 0; i < _columns.Count; i++)
{
object value1 = GetValue(x, _columns[i], _cache);
object value2 = GetValue(y, _columns[i], _cache);
if (!value1.Equals(value2)) return false;
}
return true;
}
public int GetHashCode(T obj)
{
return _enableHashCode ? GetHashCode(obj, _columns, _cache) : 0;
}
private object GetValue(object item, string propertyName, ConcurrentDictionary<string, Func<T, object>> cache)
{
if (!cache.TryGetValue(propertyName, out Func<T, object> propertyResolver))
{
ParameterExpression arg = Expression.Parameter(item.GetType(), "x");
Expression expr = Expression.Property(arg, propertyName);
UnaryExpression unaryExpression = Expression.Convert(expr, typeof(object));
propertyResolver = Expression.Lambda<Func<T, object>>(unaryExpression, arg).Compile();
cache.TryAdd(propertyName, propertyResolver);
}
return propertyResolver((T)item);
}
private int GetHashCode(T obj, List<string> columns, ConcurrentDictionary<string, Func<T, object>> cache)
{
unchecked
{
var hashCode = 17;
for (var i = 0; i < columns.Count; i++)
{
object value = GetValue(obj, columns[i], cache);
var tempHashCode = value == null ? 0 : value.GetHashCode();
hashCode = hashCode * 23 + tempHashCode;
}
return hashCode;
}
}
}