2011-01-05 1 views
42

나는이 같은, 내 자신의 사용자 정의 비교자를 사용하는 LINQ 고유() 문이 있습니다에 대한 같음 비교에 대한 대리자를 사용하여 LINQ의 고유()

class MyComparer<T> : IEqualityComparer<T> where T : MyType 
{ 
    public bool Equals(T x, T y) 
    { 
     return x.Id.Equals(y.Id); 
    } 

    public int GetHashCode(T obj) 
    { 
     return obj.Id.GetHashCode(); 
    } 
} 

... 

var distincts = bundle.GetAllThings.Distinct(new MyComparer<MySubType>()); 

이 모든 벌금과 멋쟁이와 나는 같은 일을 필요. 호기심에서, 나 자신의 Comparer을 정의해야합니까, 아니면 대리인으로 대체 할 수 있습니까? 나는 이런 식으로 뭔가를 할 수 있어야한다고 생각 :

var distincts = bundle.GetAllThings.Distinct((a,b) => a.Id == b.Id); 

그러나 이것은 컴파일되지 않습니다. 깔끔한 트릭이 있습니까?

+0

은'당신'Equals' 구현에서'y'을 x''에 널 (null)에 대한 검사를 ReferenceEquals'합니다. – nicodemus13

답변

98

Distinct는 두 번째 인수로 IEqualityComparer를 사용하므로 IEqualityComparer가 필요합니다. 하지만 대표단을 맡을 일반적인 것을 만드는 것은 그리 어렵지 않습니다. 물론 이것은 이미 MoreLINQ와 같은 다른 장소에서 구현 된 것 같습니다.

source.Distinct(Compare.By(a => a.Id)); 
+2

이렇게했으면 대리인을 사용하는 고유 한 확장 메서드를 작성할 수도 있습니다. –

+0

@David, 정확히 :-) – driis

+0

굉장합니다. LINQ가 시작된 이래로이 기능이 누락되었습니다. – Konamiman

10

Distinct은 이러한 과부하가 발생하지 않으므로 좋은 옵션입니다.

MoreLinq을 사용하면 DistinctBy 연산자를 사용할 수 있습니다.

var distincts = bundle.GetAllThings.DistinctBy(a => a.Id); 

당신은 또한 IEqualityComparer<T> 구현에 적절한 대리자를 켤 수있는 일반적인 ProjectionEqualityComparer를 작성하는 것을 고려할 수 있습니다, 하나 등은 here을 나열.

2

이 : 당신이 이런 식으로 명확하게 느낀다 경우

source.DistinctBy(a => a.Id); 

또는 : 당신이 구문을 제공

public static class Compare 
{ 
    public static IEnumerable<T> DistinctBy<T, TIdentity>(this IEnumerable<T> source, Func<T, TIdentity> identitySelector) 
    { 
     return source.Distinct(Compare.By(identitySelector)); 
    } 

    public static IEqualityComparer<TSource> By<TSource, TIdentity>(Func<TSource, TIdentity> identitySelector) 
    { 
     return new DelegateComparer<TSource, TIdentity>(identitySelector); 
    } 

    private class DelegateComparer<T, TIdentity> : IEqualityComparer<T> 
    { 
     private readonly Func<T, TIdentity> identitySelector; 

     public DelegateComparer(Func<T, TIdentity> identitySelector) 
     { 
      this.identitySelector = identitySelector; 
     } 

     public bool Equals(T x, T y) 
     { 
      return Equals(identitySelector(x), identitySelector(y)); 
     } 

     public int GetHashCode(T obj) 
     { 
      return identitySelector(obj).GetHashCode(); 
     } 
    } 
} 

:

당신은이 같은 것을 구현할 수 link은 사용자가 부여한 방식으로 Distinct를 사용할 수있는 확장 방법을 만드는 방법을 보여줍니다. 두 개의 확장 메소드 Distinct과 하나의 IEqualityComparer을 작성해야합니다.

는 다음 사이트에서 코드입니다 :

당신은이한다고
public static class Extensions 
    { 
     public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, Func<T, T, bool> comparer) 
     {   
      return source.Distinct(new DelegateComparer<T>(comparer)); 
     } 

     public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, Func<T, T, bool> comparer, Func<T,int> hashMethod) 
     { 
      return source.Distinct(new DelegateComparer<T>(comparer,hashMethod)); 
     } 
    } 

    public class DelegateComparer<T> : IEqualityComparer<T> 
    { 
     private Func<T, T, bool> _equals; 
     private Func<T,int> _getHashCode; 

     public DelegateComparer(Func<T, T, bool> equals) 
     { 
      this._equals = equals; 
     } 

     public DelegateComparer(Func<T, T, bool> equals, Func<T,int> getHashCode) 
     { 
      this._equals = equals; 
      this._getHashCode = getHashCode; 
     } 

     public bool Equals(T a, T b) 
     { 
      return _equals(a, b); 
     } 

     public int GetHashCode(T a) 
     { 
      if (_getHashCode != null)  
       return _getHashCode(a);  
      else 
       return a.GetHashCode(); 
     } 
    } 
+4

이 코드의 문제점은 GetHashCode 및 Equals에 대한 규칙을 따르지 않는다는 것입니다 (msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx 참조). 사용자 정의 클래스에서 첫 번째 오버로드를 사용할 때마다 잘못된 결과가 발생할 가능성이 큽니다. GetHashCode *는 Equals가 true를 반환 할 때 두 객체에 대해 동일한 값을 반환해야합니다. –