CSharp - lambda distinct( )?

  显示原文与译文双语对照的内容

对,所以我有一个可以枚举的,并希望从它得到不同的值。

使用 System.Linq,当然有一个名为 Distinct的扩展方法。 在简单情况下,可以不使用任何参数,比如:


var distinctValues = myStringList.Distinct();

好的,但如果我有一个可以枚举对象,我需要指定相等的对象,那么唯一可用的重载是:


var distinctValues = myCustomerList.Distinct(someEqualityComparer);

相等比较器参数必须是 IEqualityComparer<T>的实例。 我可以这样做,当然,它有点冗长,而且是 cludgy 。

我需要的是一个重载,它将采用一个 lambda,比如函数 <T,T,bool> :


var distinctValues
 = myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);

任何人都知道是否存在这样的扩展,或者某些等价的解决方法? 还是我漏掉了一些东西?

或者,是否可以指定一个IEqualityComparer内联( embarass我)?

更新

我在这个主题的MSDN论坛中找到了 Anders Hejlsberg对 post的回复。 他说:

你将遇到的问题是,当两个对象比较相等时,它们必须具有相同的GetHashCode返回值( 否则,由Distinct内部使用的哈希表将无法正常工作) 。 我们使用 IEqualityComparer,因为它将Equals和GetHashCode的兼容实现封装到一个接口中。

我觉得这很有意义。

时间:

它看起来像你想要 DistinctByMoreLINQ 。 然后你可以编写:


var distinctValues = myCustomerList.DistinctBy(c => c.CustomerId);

下面是一个cut-down版本的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;
 }
 }
}

不存在这里扩展方法重载。 我在过去发现了这个令人沮丧的问题,因此我通常编写一个 helper 类来处理这个问题。 目标是将 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));

这将执行你想要的操作,但我不知道性能:


var distinctValues =
 from cust in myCustomerList
 group cust by cust.CustomerId
 into gcust
 select gcust.First();

至少它不是冗长的。

我使用过的东西对我很有用。


///<summary>
///A class to wrap the IEqualityComparer interface into matching functions for simple implementation
///</summary>
///<typeparam name="T">The type of object to be compared</typeparam>
public class MyIEqualityComparer<T> : IEqualityComparer<T>
{
///<summary>
///Create a new comparer based on the given Equals and GetHashCode methods
///</summary>
///<param name="equals">The method to compute equals of two T instances</param>
///<param name="getHashCode">The method to compute a hashcode for a T instance</param>
 public MyIEqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
 {
 if (equals == null)
 throw new ArgumentNullException("equals","Equals parameter is required for all MyIEqualityComparer instances");
 EqualsMethod = equals;
 GetHashCodeMethod = getHashCode;
 }
///<summary>
///Gets the method used to compute equals
///</summary>
 public Func<T, T, bool> EqualsMethod { get; private set; }
///<summary>
///Gets the method used to compute a hash code
///</summary>
 public Func<T, int> GetHashCodeMethod { get; private set; }

 bool IEqualityComparer<T>.Equals(T x, T y)
 {
 return EqualsMethod(x, y);
 }

 int IEqualityComparer<T>.GetHashCode(T obj)
 {
 if (GetHashCodeMethod == null)
 return obj.GetHashCode();
 return GetHashCodeMethod(obj);
 }
}

下面是一个简单的扩展方法,它做我需要的。


public static class EnumerableExtensions
{
 public static IEnumerable<TKey> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> selector)
 {
 return source.GroupBy(selector).Select(x => x.Key);
 }
}

不幸的是他们没有在框架中烘焙不同的方法,但是嘿。

我在这里看到的所有解决方案都依赖于选择一个可以比较的字段。 但是,如果需要以不同的方式进行比较,这里的这个解决方案似乎可以正常工作,比如:


somedoubles.Distinct(new LambdaComparer<double>((x, y) => Math.Abs(x - y) <double.Epsilon)).Count()

如果 Distinct() 不产生唯一结果,请尝试:


var filteredWC = tblWorkCenter.GroupBy(cc => cc.WCID_I).Select(grp => grp.First()).Select(cc => new Model.WorkCenter { WCID = cc.WCID_I }).OrderBy(cc => cc.WCID); 

ObservableCollection<Model.WorkCenter> WorkCenter = new ObservableCollection<Model.WorkCenter>(filteredWC);

...