对,所以我有一个可枚举的,希望从中得到不同的值。
使用System.Linq,当然有一个名为Distinct的扩展方法。在简单的情况下,它可以不带参数使用,例如:
var distinctValues = myStringList.Distinct();
很好,但如果我有一个可枚举的对象,需要为其指定相等,唯一可用的重载是:
var distinctValues = myCustomerList.Distinct(someEqualityComparer);
相等比较器参数必须是IEqualityComparer<T>的实例。当然,我可以做到这一点,但这有点冗长,而且,嗯,包含了很多内容。
我所期望的是一个重载,它会接受lambda,比如Func<T,T,bool>:
var distinctValues = myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);
有人知道是否存在这样的扩展,或者是否存在等效的解决方法?还是我错过了什么?
或者,有没有一种方法可以内联指定IEqualityComparer(让我尴尬)?
使现代化
我在MSDN论坛上找到了Anders Hejlsberg对此主题的回复。他说:
您将遇到的问题是,当两个对象进行比较时相等,它们必须具有相同的GetHashCode返回值(或Distinct内部使用的哈希表将无法正常工作)。我们使用IEqualityComparer,因为它包兼容Equals和GetHashCode的实现集成到单个接口中。
我想这是有道理的。
总结一下。我想大多数像我一样来到这里的人都希望得到最简单的解决方案,而不使用任何库,并且性能最好。
(我认为,在我看来,按方法分组是一种过度的表现。)
这里有一个使用IEqualityComparer接口的简单扩展方法,它也适用于空值。
用法:
var filtered = taskList.DistinctBy(t => t.TaskExternalId).ToArray();
扩展方法代码
public static class LinqExtensions
{
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
{
GeneralPropertyComparer<T, TKey> comparer = new GeneralPropertyComparer<T,TKey>(property);
return items.Distinct(comparer);
}
}
public class GeneralPropertyComparer<T,TKey> : IEqualityComparer<T>
{
private Func<T, TKey> expr { get; set; }
public GeneralPropertyComparer (Func<T, TKey> expr)
{
this.expr = expr;
}
public bool Equals(T left, T right)
{
var leftProp = expr.Invoke(left);
var rightProp = expr.Invoke(right);
if (leftProp == null && rightProp == null)
return true;
else if (leftProp == null ^ rightProp == null)
return false;
else
return leftProp.Equals(rightProp);
}
public int GetHashCode(T obj)
{
var prop = expr.Invoke(obj);
return (prop==null)? 0:prop.GetHashCode();
}
}
不,对此没有此类扩展方法重载。过去我发现这让我很沮丧,因此我通常会编写一个助手类来处理这个问题。目标是将Func<T,T,bool>转换为IEqualityComparer<T,T>。
实例
public class EqualityFactory {
private sealed class Impl<T> : IEqualityComparer<T,T> {
private Func<T,T,bool> m_del;
private IEqualityComparer<T> m_comp;
public Impl(Func<T,T,bool> del) {
m_del = del;
m_comp = EqualityComparer<T>.Default;
}
public bool Equals(T left, T right) {
return m_del(left, right);
}
public int GetHashCode(T value) {
return m_comp.GetHashCode(value);
}
}
public static IEqualityComparer<T,T> Create<T>(Func<T,T,bool> del) {
return new Impl<T>(del);
}
}
这允许您编写以下内容
var distinctValues = myCustomerList
.Distinct(EqualityFactory.Create((c1, c2) => c1.CustomerId == c2.CustomerId));
总结一下。我想大多数像我一样来到这里的人都希望得到最简单的解决方案,而不使用任何库,并且性能最好。
(我认为,在我看来,按方法分组是一种过度的表现。)
这里有一个使用IEqualityComparer接口的简单扩展方法,它也适用于空值。
用法:
var filtered = taskList.DistinctBy(t => t.TaskExternalId).ToArray();
扩展方法代码
public static class LinqExtensions
{
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
{
GeneralPropertyComparer<T, TKey> comparer = new GeneralPropertyComparer<T,TKey>(property);
return items.Distinct(comparer);
}
}
public class GeneralPropertyComparer<T,TKey> : IEqualityComparer<T>
{
private Func<T, TKey> expr { get; set; }
public GeneralPropertyComparer (Func<T, TKey> expr)
{
this.expr = expr;
}
public bool Equals(T left, T right)
{
var leftProp = expr.Invoke(left);
var rightProp = expr.Invoke(right);
if (leftProp == null && rightProp == null)
return true;
else if (leftProp == null ^ rightProp == null)
return false;
else
return leftProp.Equals(rightProp);
}
public int GetHashCode(T obj)
{
var prop = expr.Invoke(obj);
return (prop==null)? 0:prop.GetHashCode();
}
}