对,所以我有一个可枚举的,希望从中得到不同的值。
使用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的实现集成到单个接口中。
我想这是有道理的。
您可以使用InlineComparer
public class InlineComparer<T> : IEqualityComparer<T>
{
//private readonly Func<T, T, bool> equalsMethod;
//private readonly Func<T, int> getHashCodeMethod;
public Func<T, T, bool> EqualsMethod { get; private set; }
public Func<T, int> GetHashCodeMethod { get; private set; }
public InlineComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
{
if (equals == null) throw new ArgumentNullException("equals", "Equals parameter is required for all InlineComparer instances");
EqualsMethod = equals;
GetHashCodeMethod = hashCode;
}
public bool Equals(T x, T y)
{
return EqualsMethod(x, y);
}
public int GetHashCode(T obj)
{
if (GetHashCodeMethod == null) return obj.GetHashCode();
return GetHashCodeMethod(obj);
}
}
使用示例:
var comparer = new InlineComparer<DetalleLog>((i1, i2) => i1.PeticionEV == i2.PeticionEV && i1.Etiqueta == i2.Etiqueta, i => i.PeticionEV.GetHashCode() + i.Etiqueta.GetHashCode());
var peticionesEV = listaLogs.Distinct(comparer).ToList();
Assert.IsNotNull(peticionesEV);
Assert.AreNotEqual(0, peticionesEV.Count);
资料来源:https://stackoverflow.com/a/5969691/206730使用IEqualityComparer for Union我可以内联指定显式类型比较器吗?
IEnumerable lambda扩展名:
public static class ListExtensions
{
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list, Func<T, int> hashCode)
{
Dictionary<int, T> hashCodeDic = new Dictionary<int, T>();
list.ToList().ForEach(t =>
{
var key = hashCode(t);
if (!hashCodeDic.ContainsKey(key))
hashCodeDic.Add(key, t);
});
return hashCodeDic.Select(kvp => kvp.Value);
}
}
用法:
class Employee
{
public string Name { get; set; }
public int EmployeeID { get; set; }
}
//Add 5 employees to List
List<Employee> lst = new List<Employee>();
Employee e = new Employee { Name = "Shantanu", EmployeeID = 123456 };
lst.Add(e);
lst.Add(e);
Employee e1 = new Employee { Name = "Adam Warren", EmployeeID = 823456 };
lst.Add(e1);
//Add a space in the Name
Employee e2 = new Employee { Name = "Adam Warren", EmployeeID = 823456 };
lst.Add(e2);
//Name is different case
Employee e3 = new Employee { Name = "adam warren", EmployeeID = 823456 };
lst.Add(e3);
//Distinct (without IEqalityComparer<T>) - Returns 4 employees
var lstDistinct1 = lst.Distinct();
//Lambda Extension - Return 2 employees
var lstDistinct = lst.Distinct(employee => employee.EmployeeID.GetHashCode() ^ employee.Name.ToUpper().Replace(" ", "").GetHashCode());
在我看来,你想从MoreLINQ得到DistinctBy。然后你可以写:
var distinctValues = myCustomerList.DistinctBy(c => c.CustomerId);
这里是DistinctBy的简化版本(没有无效性检查,也没有指定自己的密钥比较器的选项):
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> knownKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (knownKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
不,对此没有此类扩展方法重载。过去我发现这让我很沮丧,因此我通常会编写一个助手类来处理这个问题。目标是将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));