12

对实现大量 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);
    }
}

看起来不错,但是每次都提供哈希函数真的有必要吗?我了解哈希码用于将对象放入存储桶中。不同的bucket,object不相等,不调用equal。

如果 GetHashCode 返回相同的值,则调用 equals。(来自:为什么在重写 Equals 方法时重写 GetHashCode 很重要?

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

4

6 回答 6

13

什么都不会出错,但是在基于哈希表的容器中,在进行查找时,您的性能会从大约 O(1) 变为 O(n)。您最好将所有内容简单地存储在 List 中并蛮力搜索它以查找满足相等性的项目。

于 2011-05-06T09:18:59.320 回答
10

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

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);

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

编辑:一个更有效和更强大的实现激发了我的马克的评论:

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); 
于 2011-05-06T09:27:29.480 回答
1

你的表现将付诸东流。Distinct并且Except在集合数据结构上实现时是有效的操作。通过提供一个恒定的哈希值,您基本上会破坏此特性并使用线性搜索强制执行简单算法。

您需要查看这对于您的数据量是否可以接受。但是对于更大的数据集,差异会很明显。例如,Except将从预期时间 O( n ) 增加到 O( ),这可能很重要。

GetHashCode为什么不直接调用对象自己的方法,而不是提供一个常量呢?它可能不会给出一个特别好的值,但它不会比使用常量更差,并且除非GetHashCode对象的方法被覆盖以返回错误值,否则仍然会保持正确性。

于 2011-05-06T09:22:20.520 回答
1

在 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
}  
于 2014-03-05T13:12:16.567 回答
0

我需要将 Henrik 解决方案重写为一个实现的类IEqualityComparer,它给出了:

    public class GenericEqualityComparer<T,TKey> : IEqualityComparer<T>
    {
        private readonly Func<T, TKey> _keyFunction;

        public GenericEqualityComparer(Func<T, TKey> keyFunction)
        {
            _keyFunction = keyFunction;
        }

        public bool Equals(T x, T y) => EqualityComparer<TKey>.Default.Equals(_keyFunction(x), _keyFunction(y));

        public int GetHashCode(T obj)=> EqualityComparer<TKey>.Default.GetHashCode(_keyFunction(obj));
    }
于 2020-03-10T10:46:19.820 回答
-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();

于 2014-05-13T06:12:23.990 回答