2011-01-05 4 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'の実装に 'X'と' y'のヌルに対する 'ReferenceEquals'チェックを有するべきです。 – nicodemus13

答えて

98

Distinctは、第2引数としてIEqualityComparerをとります。したがって、IEqualityComparerが必要です。しかし、デリゲートを取る一般的なものを作るのは難しくありません。もちろん、これはすでにMoreLINQのようないくつかの場所で実装されている可能性があります。

あなたはそれをこのような何かを実装できます。

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(); 
     } 
    } 
} 

あなたの構文を与える:

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

それとも、あなたはそれが明確だと感じている場合、この方法:

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); 

また、このようなhereを記載されているものとして、IEqualityComparer<T>実装に適切なデリゲートを回すことができ、一般的なProjectionEqualityComparerを書いて検討する必要があります。

2

これをlinkは、あなたが与えた方法でDistinctを使用できるように拡張メソッドを作成する方法を示しています。 2つの拡張方法、および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を返すときに2つのオブジェクトに対して同じ値を返す必要があります。 –

関連する問題