首页 文章

通用IEqualityComparer <T>和GetHashCode

提问于
浏览
10

对于实现大量的IEqualityComparers有些懒惰,并且考虑到我无法轻松编辑被比较对象的类实现,我使用了以下内容,意在与Distinct()和Except()扩展方法一起使用 . :

public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
    Func<T, T, bool> compareFunction;
    Func<T, int> hashFunction;

    public GenericEqualityComparer(Func<T, T, bool> compareFunction, Func<T, int> hashFunction)
    {
        this.compareFunction = compareFunction;
        this.hashFunction = hashFunction;
    }

    public bool Equals(T x, T y)
    {
        return compareFunction(x, y);
    }

    public int GetHashCode(T obj)
    {
        return hashFunction(obj);
    }
}

看起来很不错,但每次真的需要一个哈希函数吗?我知道哈希码用于将对象放入存储桶中 . 不同的桶,对象不相等,并且不调用相等 .

如果GetHashCode返回相同的值,则调用equals . (来自:Why is it important to override GetHashCode when Equals method is overridden?

所以可能出现问题,例如(我听到很多程序员惊恐地尖叫),GetHashCode返回一个常量,强制调用Equal?

5 回答

  • -1

    没有什么会出错,但在基于散列表的容器中,在进行查找时,您将从大约O(1)到O(n)性能 . 简单地将所有内容存储在List中并蛮力地搜索它以查找满足相等性的项目 .

  • 1

    如果一个常见的用例是根据一个属性比较对象,你可以添加一个额外的构造函数并实现并调用它:

    public GenericEqualityComparer(Func<T, object> projection)
    {
        compareFunction = (t1, t2) => projection(t1).Equals(projection(t2));
        hashFunction = t => projection(t).GetHashCode();
    }
    
    var comaparer = new GenericEqualityComparer( o => o.PropertyToCompare);
    

    这将自动使用属性实现的哈希 .

    编辑:一个更有效和更强大的实现启发了我的Marc评论:

    public static GenericEqualityComparer<T> Create<TValue>(Func<T, TValue> projection)
    {
        return new GenericEqualityComparer<T>(
            (t1, t2) => EqualityComparer<TValue>.Default.Equals( projection(t1), projection(t2)),
            t => EqualityComparer<TValue>.Default.GetHashCode(projection(t)));
    }
    
    var comparer = GenericEqualityComparer<YourObjectType>.Create( o => o.PropertyToCompare);
    
  • 13

    你的表现将会消失 . DistinctExcept 在集合数据结构上实现时是高效的操作 . 通过提供一个恒定的哈希值,你基本上破坏了这个特性,并使用线性搜索强制简单算法 .

    您需要查看这是否适用于您的数据量 . 但对于稍微大一些的数据集,差异将会很明显 . 例如, Except 将从预期时间O(n)增加到O(n²),这可能是一个大问题 .

    而不是提供常量,为什么不调用对象自己的 GetHashCode 方法?它可能没有给出特别好的值,但它不能比使用常量更差,并且除非重写对象的 GetHashCode 方法以返回错误的值,否则仍将保留正确性 .

  • 9

    在CodeProject上找到了这个 - A Generic IEqualityComparer for Linq Distinct()做得很好 .

    使用案例:

    IEqualityComparer<Contact> c =  new PropertyComparer<Contact>("Name");
    IEnumerable<Contact> distinctEmails = collection.Distinct(c);
    

    通用IEqualityComparer

    public class PropertyComparer<T> : IEqualityComparer<T>
    {
        private PropertyInfo _PropertyInfo;
    
        /// <summary>
        /// Creates a new instance of PropertyComparer.
        /// </summary>
        /// <param name="propertyName">The name of the property on type T 
        /// to perform the comparison on.</param>
        public PropertyComparer(string propertyName)
        {
            //store a reference to the property info object for use during the comparison
            _PropertyInfo = typeof(T).GetProperty(propertyName, 
        BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
            if (_PropertyInfo == null)
            {
                throw new ArgumentException(string.Format("{0} 
            is not a property of type {1}.", propertyName, typeof(T)));
            }
        }
    
        #region IEqualityComparer<T> Members
    
        public bool Equals(T x, T y)
        {
            //get the current value of the comparison property of x and of y
            object xValue = _PropertyInfo.GetValue(x, null);
            object yValue = _PropertyInfo.GetValue(y, null);
    
            //if the xValue is null then we consider them equal if and only if yValue is null
            if (xValue == null)
                return yValue == null;
    
            //use the default comparer for whatever type the comparison property is.
            return xValue.Equals(yValue);
        }
    
        public int GetHashCode(T obj)
        {
            //get the value of the comparison property out of obj
            object propertyValue = _PropertyInfo.GetValue(obj, null);
    
            if (propertyValue == null)
                return 0;
    
            else
                return propertyValue.GetHashCode();
        }
    
        #endregion
    }
    
  • 1

    试试这段代码:

    public class GenericCompare<T> : IEqualityComparer<T> where T : class
    {
        private Func<T, object> _expr { get; set; }
        public GenericCompare(Func<T, object> expr)
        {
            this._expr = expr;
        }
        public bool Equals(T x, T y)
        {
            var first = _expr.Invoke(x);
            var sec = _expr.Invoke(y);
            if (first != null && first.Equals(sec))
                return true;
            else
                return false;
        }
        public int GetHashCode(T obj)
        {
            return obj.GetHashCode();
        }
    }
    

    示例:collection = collection.Except(ExistedDataEles,new GenericCompare(x => x.Id)) . ToList();

相关问题