GetHashCode为不同的对象返回相同的值。是否有通过特定属性标识对象的方法?

时间:2019-01-05 08:24:44

标签: c# hashcode gethashcode

我正在尝试创建一个哈希码方法。我有如下代码:

    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不保证唯一值,如何通过特定属性标识对象?

3 个答案:

答案 0 :(得分:0)

我怀疑问题是由您的value.GetHashCode()方法中的GetHashCode<T>引起的。该值变量在那里是一个对象,我认为GetHashCode()不会返回您期望的结果。尝试调试以找出正在发生的情况。

您可能想尝试保留代码,但请使用Object.GetHashCode()(来自名称空间RuntimeHelpers.GetHashCode())而不是System.Runtime.CompilerServices

此处完整参考:https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.runtimehelpers.gethashcode?redirectedfrom=MSDN&view=netframework-4.7.2#System_Runtime_CompilerServices_RuntimeHelpers_GetHashCode_System_Object_

祝你好运!

答案 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;
            }
        }
    }