通用IEqualityComparer <T>和GetHashCode

mat*_*ieu 10 c# gethashcode

对于实现大量的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

spe*_*der 13

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


Hen*_*rik 9

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

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);
Run Code Online (Sandbox Code Playgroud)

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

编辑:一个更有效和更强大的实现启发了我的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); 
Run Code Online (Sandbox Code Playgroud)

  • @Henrik - 例如调用`.GetHashCode()`; p即`foo => foo.Name`是有风险的.内置的相等比较器避免了这种情况(避免装箱,并支持`IEquatable <T>`,并支持`Nullable <T>`) (2认同)