通用IEqualityComparer <T>和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);
}
}
看起来不错,但是每次都提供散列函数是非常必要的? 我明白哈希码用于将对象放入桶中。 不同的桶,对象不相等,相等不被调用。
如果GetHashCode返回相同的值,则调用equals。 (来自:为什么当重写Equals方法时重写GetHashCode很重要?)
因此,如果例如(我听到很多程序员惊恐地尖叫)会出现什么问题,GetHashCode返回一个常量,以强制调用Equal?
没有什么会出错,但是在基于散列表的容器中,执行查找时,您将从大约O(1)到O(n)的性能。 你最好将所有内容都存储在一个列表中,并用蛮力搜索满足平等的项目。
如果一个常见的用例是根据它们的一个属性比较对象,那么可以添加一个额外的构造函数并实现,并像这样调用它:
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);
你的表现将会下降。 当在集合数据结构上实现时, Distinct
和Except
是有效的操作。 通过提供一个恒定的散列值,你基本上可以摧毁这个特性,并使用线性搜索来迫使朴素算法。
您需要查看这是否适用于您的数据量。 但对于较大的数据集,差异将会显着。 例如, Except
预期的时间O(n)增加到O(n2)之外,这可能是一个大问题。
为什么不直接调用对象自己的GetHashCode
方法而不是提供常量? 它可能不会给出特别好的值,但它不会比使用常量更差,并且除非对象的GetHashCode
方法被重写以返回错误的值,否则正确性仍将保留。