在实现大量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?
答案 0 :(得分:13)
没有什么会出错,但在基于散列表的容器中,在进行查找时,您将从大约O(1)到O(n)性能。你最好只是将所有东西都存储在List中,然后蛮力地搜索它以获得相同的项目。
答案 1 :(得分: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);
这将自动使用属性实现的哈希。
编辑:一个更高效,更强大的实现激发了我对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);
答案 2 :(得分:1)
你的表现将会消失。在集合数据结构上实现时,Distinct
和Except
是高效的操作。通过提供一个恒定的哈希值,你基本上会破坏这个特性,并使用线性搜索强制简单算法。
您需要查看这是否适用于您的数据量。但对于稍微大一些的数据集,差异将会很明显。例如,Except
将从预期时间O( n )增加到O( n ²),这可能是一个大问题。
为什么不调用对象自己的GetHashCode
方法,而不是提供常量?它可能没有给出特别好的值,但它不能比使用常量更糟糕,除非重写对象的GetHashCode
方法以返回错误的值,否则仍将保留正确性。
答案 3 :(得分: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
}
答案 4 :(得分: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));
}
答案 5 :(得分:-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 =&gt; x.Id))。ToList();