Bir temsilciyi bir IEqualityComparer'a sarın


127

Birkaç Linq.Enumerable işlevi bir IEqualityComparer<T>. A'yı delegate(T,T)=>booluygulamaya uyarlayan uygun bir sarmalayıcı sınıfı var IEqualityComparer<T>mı? Bir tane yazmak yeterince kolaydır (doğru bir karma kodu tanımlamayla ilgili sorunları görmezden gelirseniz), ancak kullanıma hazır bir çözüm olup olmadığını bilmek istiyorum.

Özellikle, Dictionaryüyeliği tanımlamak için yalnızca Anahtarları kullanarak (değerleri farklı kurallara göre korurken) s üzerinde set işlemleri yapmak istiyorum .

Yanıtlar:


44

Normalde, cevaba @ Sam yorumlayarak bu sorunu çözerdim (orijinal gönderide, davranışı değiştirmeden biraz temizlemek için bazı düzenlemeler yaptım.)

Aşağıda, varsayılan hashing politikasında bir [IMNSHO] kritik düzeltme ile @ Sam'in cevabı riffim: -

class FuncEqualityComparer<T> : IEqualityComparer<T>
{
    readonly Func<T, T, bool> _comparer;
    readonly Func<T, int> _hash;

    public FuncEqualityComparer( Func<T, T, bool> comparer )
        : this( comparer, t => 0 ) // NB Cannot assume anything about how e.g., t.GetHashCode() interacts with the comparer's behavior
    {
    }

    public FuncEqualityComparer( Func<T, T, bool> comparer, Func<T, int> hash )
    {
        _comparer = comparer;
        _hash = hash;
    }

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

    public int GetHashCode( T obj )
    {
        return _hash( obj );
    }
}

5
Bana kalırsa bu doğru cevap. Herhangi IEqualityComparer<T>yapraklar o GetHashCodedışarı sadece düz bozuldu.
Dan Tao

1
@Joshua Frank: Eşitliği ifade etmek için hash eşitliğini kullanmak geçerli değildir - yalnızca tersi doğrudur. Kısacası, @Dan Tao söylediği şeyde tamamen doğrudur ve bu cevap, bu gerçeğin daha önce tamamlanmamış bir cevaba uygulanmasıdır
Ruben Bartelink

2
@Ruben Bartelink: Açıkladığınız için teşekkürler. Ama yine de t => 0 karma politikanızı anlamıyorum. Tüm nesneler her zaman aynı şeye (sıfır) hashleme yapıyorsa, bu, @Dan Tao'nun bakış açısına göre obj.GetHashCode'u kullanmaktan daha da bozuk değil mi? Neden arayan kişiyi her zaman iyi bir hash işlevi sağlamaya zorlamayasınız?
Joshua Frank

1
Bu nedenle, sağlandığı bir Func'daki gelişigüzel bir algoritmanın, karma kodların farklı olmasına rağmen muhtemelen doğruya dönmeyeceğini varsaymak mantıklı değildir. Her zaman sıfıra geri dönmenin hash işlemi olmadığını düşündüğünüz nokta doğru değil. Bu nedenle profil oluşturucu aramaların yeterince verimli olmadığını söylediğinde hashing Func'u alan bir aşırı yüklenme vardır. Tüm bunlardaki tek nokta, varsayılan bir hash algoritmasına sahip olacaksanız, her zaman çalışan ve yüzeysel olarak tehlikeli davranışlara sahip olmayan bir algoritma olması gerektiğidir. Ve sonra performans üzerinde çalışabiliriz!
Ruben Bartelink

4
Başka bir deyişle, özel bir karşılaştırıcı kullandığınızdan , nesnenin varsayılan karşılaştırıcıyla ilgili varsayılan karma koduyla hiçbir ilgisi yoktur , bu nedenle onu kullanamazsınız.
Peet Brits

170

Önemi hakkında GetHashCode

Diğerleri, herhangi bir özel IEqualityComparer<T>uygulamanın gerçekten bir GetHashCodeyöntem içermesi gerektiği gerçeğini zaten yorumlamışlardır ; ama kimse nedenini detaylı bir şekilde açıklamaya zahmet etmedi .

İşte nedeni. Sorunuz özellikle LINQ uzatma yöntemlerinden bahseder; Neredeyse bunların hepsi , verimlilik için dahili olarak karma tablolar kullandığından, düzgün çalışması için karma kodlara güvenir.

Örneğin alın Distinct. Kullanılan tek şey bir yöntemse, bu uzantı yönteminin sonuçlarını düşünün Equals. Bir öğenin sıralı olarak taranıp taranmadığını nasıl anlarsınız Equals? Daha önce baktığınız tüm değerler koleksiyonunu numaralandırır ve bir eşleşme olup olmadığını kontrol edersiniz. Bu, DistinctO (N ) bir algoritma yerine en kötü durum O (N 2 ) algoritmasının kullanılmasıyla sonuçlanır!

Neyse ki, durum bu değil. Distinctgelmez sadece kullanmak Equals; o da kullanır GetHashCode. Aslında, bir düzgün sağlayan bir olmadan kesinlikle düzgün çalışmazIEqualityComparer<T>GetHashCode . Aşağıda bunu gösteren yapmacık bir örnek var.

Aşağıdaki türe sahip olduğumu varsayalım:

class Value
{
    public string Name { get; private set; }
    public int Number { get; private set; }

    public Value(string name, int number)
    {
        Name = name;
        Number = number;
    }

    public override string ToString()
    {
        return string.Format("{0}: {1}", Name, Number);
    }
}

Şimdi bir a'ya sahip olduğumu List<Value>ve tüm öğeleri farklı bir ada sahip bulmak istediğimi söyleyin . Bu, Distinctözel bir eşitlik karşılaştırıcısı kullanmak için mükemmel bir kullanım örneğidir . O halde Aku'nun cevabındakiComparer<T> sınıfı kullanalım :

var comparer = new Comparer<Value>((x, y) => x.Name == y.Name);

Şimdi, Valueaynı Nameözelliğe sahip bir grup öğeye sahipsek , bunların tümü ile döndürülen tek bir değere daraltılmalıdır Distinct, değil mi? Bakalım...

var values = new List<Value>();

var random = new Random();
for (int i = 0; i < 10; ++i)
{
    values.Add("x", random.Next());
}

var distinct = values.Distinct(comparer);

foreach (Value x in distinct)
{
    Console.WriteLine(x);
}

Çıktı:

x: 1346013431
x: 1388845717
x: 1576754134
x: 1104067189
x: 1144789201
x: 1862076501
x: 1573781440
x: 646797592
x: 655632802
x: 1206819377

Hmm, bu işe yaramadı, değil mi?

Peki ya GroupBy? Hadi deneyelim:

var grouped = values.GroupBy(x => x, comparer);

foreach (IGrouping<Value> g in grouped)
{
    Console.WriteLine("[KEY: '{0}']", g);
    foreach (Value x in g)
    {
        Console.WriteLine(x);
    }
}

Çıktı:

[KEY = 'x: 1346013431']
x: 1346013431
[ANAHTAR = 'x: 1388845717']
x: 1388845717
[KEY = 'x: 1576754134']
x: 1576754134
[KEY = 'x: 1104067189']
x: 1104067189
[KEY = 'x: 1144789201']
x: 1144789201
[KEY = 'x: 1862076501']
x: 1862076501
[KEY = 'x: 1573781440']
x: 1573781440
[KEY = 'x: 646797592']
x: 646797592
[KEY = 'x: 655632802']
x: 655632802
[KEY = 'x: 1206819377']
x: 1206819377

Yine: işe yaramadı.

Düşünürseniz, dahili olarak Distinctbir HashSet<T>(veya eşdeğerini) GroupBykullanmak ve dahili olarak bir gibi bir şey kullanmak mantıklı olacaktır Dictionary<TKey, List<T>>. Bu, bu yöntemlerin neden işe yaramadığını açıklayabilir mi? Hadi bunu deneyelim:

var uniqueValues = new HashSet<Value>(values, comparer);

foreach (Value x in uniqueValues)
{
    Console.WriteLine(x);
}

Çıktı:

x: 1346013431
x: 1388845717
x: 1576754134
x: 1104067189
x: 1144789201
x: 1862076501
x: 1573781440
x: 646797592
x: 655632802
x: 1206819377

Evet ... mantıklı gelmeye mi başladın?

Umarım bu örneklerden GetHashCode, herhangi bir IEqualityComparer<T>uygulamaya uygun bir uygulamanın dahil edilmesinin neden bu kadar önemli olduğu açıktır.


Orijinal cevap

Orip'in cevabını genişletmek için :

Burada yapılabilecek birkaç iyileştirme var.

  1. İlk olarak, Func<T, TKey>yerine a alırdım Func<T, object>; bu, değer türü anahtarların keyExtractorkendi içinde kutulanmasını önleyecektir .
  2. İkinci olarak, aslında bir where TKey : IEquatable<TKey>sınırlama eklerdim; bu, Equalsçağrıda kutulamayı önleyecektir ( object.Equalsbir objectparametre alır ; IEquatable<TKey>bir TKeyparametreyi kutulamadan almak için bir uygulamaya ihtiyacınız vardır ). Açıkçası bu çok ciddi bir kısıtlama oluşturabilir, dolayısıyla kısıtlama olmadan bir temel sınıf ve onunla türetilmiş bir sınıf yapabilirsiniz.

Sonuç olarak ortaya çıkan kod şöyle görünebilir:

public class KeyEqualityComparer<T, TKey> : IEqualityComparer<T>
{
    protected readonly Func<T, TKey> keyExtractor;

    public KeyEqualityComparer(Func<T, TKey> keyExtractor)
    {
        this.keyExtractor = keyExtractor;
    }

    public virtual bool Equals(T x, T y)
    {
        return this.keyExtractor(x).Equals(this.keyExtractor(y));
    }

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

public class StrictKeyEqualityComparer<T, TKey> : KeyEqualityComparer<T, TKey>
    where TKey : IEquatable<TKey>
{
    public StrictKeyEqualityComparer(Func<T, TKey> keyExtractor)
        : base(keyExtractor)
    { }

    public override bool Equals(T x, T y)
    {
        // This will use the overload that accepts a TKey parameter
        // instead of an object parameter.
        return this.keyExtractor(x).Equals(this.keyExtractor(y));
    }
}

1
Kişisel StrictKeyEqualityComparer.Equalsyöntem ile aynı gibi görünüyor KeyEqualityComparer.Equals. Does TKey : IEquatable<TKey>kısıt yapmak TKey.Equalsfarklı çalışır?
Justin Morgan

2
@JustinMorgan: Evet - ilk durumda, TKeyherhangi bir rasgele tür olabileceğinden, derleyici Object.Equals, değer türü parametrelerinin kutulanmasını gerektiren sanal yöntemi kullanacaktır, örn int. İkinci durumda, bununla birlikte, TKeyuygulanması kısıtlandığından IEquatable<TKey>, TKey.Equalsherhangi bir kutulama gerektirmeyen yöntem kullanılacaktır.
Dan Tao

2
Çok ilginç, bilgi için teşekkürler. GetHashCode'un bu yanıtları görene kadar bu LINQ etkilerine sahip olduğunu bilmiyordum. İleride kullanmak için bilmek harika.
Justin Morgan

1
@JohannesH: Muhtemelen! İhtiyacı da ortadan kaldırırdı StringKeyEqualityComparer<T, TKey>.
Dan Tao

1
+1 @DanTao: .Net'te eşitliği tanımlarken neden karma kodları asla göz ardı etmemesi gerektiğine dair harika bir açıklama için teşekkür ederiz.
Marcelo Cantos

118

Eşitlik denetimini özelleştirmek istediğinizde, karşılaştırmanın kendisini değil, karşılaştırmak için anahtarları tanımlamakla ilgilendiğiniz zamanın% 99'u.

Bu zarif bir çözüm olabilir (Python'un liste sıralama yönteminden bir konsept ).

Kullanımı:

var foo = new List<string> { "abc", "de", "DE" };

// case-insensitive distinct
var distinct = foo.Distinct(new KeyEqualityComparer<string>( x => x.ToLower() ) );

KeyEqualityComparersınıfı:

public class KeyEqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, object> keyExtractor;

    public KeyEqualityComparer(Func<T,object> keyExtractor)
    {
        this.keyExtractor = keyExtractor;
    }

    public bool Equals(T x, T y)
    {
        return this.keyExtractor(x).Equals(this.keyExtractor(y));
    }

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

3
Bu, aku'nun cevabından çok daha iyi.
SLaks

Kesinlikle doğru yaklaşım. Kendi cevabımda bahsettiğim bence yapılabilecek birkaç iyileştirme var.
Dan Tao

1
Bu çok zarif bir kod ama soruyu cevaplamıyor, bu yüzden @ aku'nun cevabını kabul ettim. Func <T, T, bool> için bir sarmalayıcı istedim ve anahtar sözlüğümde zaten ayrılmış olduğundan bir anahtar çıkarmaya gerek yok.
Marcelo Cantos

6
@Marcelo: Sorun değil, bunu yapabilirsiniz; ancak @ aku'nun yaklaşımını kullanacaksanız, bir değer için hash kodunu sağlamak için gerçekten bir eklemeniz gerektiğini unutmayın (örneğin, Ruben'in cevabında önerildiği gibi ). Aksi takdirde, bıraktığınız uygulama, özellikle LINQ uzatma yöntemlerinde kullanışlılığı açısından oldukça bozuktur . Bunun neden olduğuna dair bir tartışma için cevabıma bakın. Func<T, int>TIEqualityComparer<T>
Dan Tao

Bu güzel, ancak seçilen anahtar bir değer türü olsaydı, gereksiz kutulama olurdu. Belki de anahtarı tanımlamak için bir TKey'ye sahip olmak daha iyi olur.
Graham Ambrose

48

Korkarım kullanıma hazır böyle bir ambalaj kağıdı yok. Ancak bir tane oluşturmak zor değil:

class Comparer<T>: IEqualityComparer<T>
{
    private readonly Func<T, T, bool> _comparer;

    public Comparer(Func<T, T, bool> comparer)
    {
        if (comparer == null)
            throw new ArgumentNullException("comparer");

        _comparer = comparer;
    }

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

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

...

Func<int, int, bool> f = (x, y) => x == y;
var comparer = new Comparer<int>(f);
Console.WriteLine(comparer.Equals(1, 1));
Console.WriteLine(comparer.Equals(1, 2));

1
Ancak, GetHashCode uygulamasına dikkat edin. Aslında onu bir tür karma tabloda kullanacaksanız, biraz daha sağlam bir şey isteyeceksiniz.
thecoop

46
bu kodun ciddi bir sorunu var! bu karşılaştırıcı açısından eşit olan ancak farklı karma kodlara sahip iki nesneye sahip bir sınıf bulmak kolaydır.
empi

10
Bunu düzeltmek için, sınıfın private readonly Func<T, int> _hashCodeResolveryapıcıda da iletilmesi ve GetHashCode(...)yöntemde kullanılması gereken başka bir üyeye ihtiyacı vardır .
herzmeister

6
Merak ediyorum: Neden obj.ToString().ToLower().GetHashCode()yerine kullanıyorsun obj.GetHashCode()?
Justin Morgan

3
IEqualityComparer<T>Çerçevedeki, perde arkasında her zaman kullanım karması kullanan yerler (örneğin, LINQ's GroupBy, Distinct, Except, Join, vb.) Ve hashing ile ilgili MS sözleşmesi bu uygulamada bozulmuştur. İşte MS'nin dokümantasyon alıntı: "Equals yöntemi, iki nesne x ve y için true döndürürse, x için GetHashCode yöntemi tarafından döndürülen değerin y için döndürülen değere eşit olması gerektiğinden emin olmak için uygulamalar gereklidir." Bakınız: msdn.microsoft.com/en-us/library/ms132155
devgeezer

22

Dan Tao'nun cevabıyla aynı, ancak birkaç iyileştirme var:

  1. EqualityComparer<>.DefaultGerçek karşılaştırmanın yapılmasına güvenir, böylece structuygulanan değer türleri için kutudan kaçınır IEquatable<>.

  2. EqualityComparer<>.DefaultKullanıldığından beri patlamaz null.Equals(something).

  3. Comparer IEqualityComparer<>örneğini oluşturmak için statik bir yönteme sahip olacak statik sarmalayıcı sağlanmıştır - aramayı kolaylaştırır. Karşılaştırmak

    Equality<Person>.CreateComparer(p => p.ID);

    ile

    new EqualityComparer<Person, int>(p => p.ID);
  4. IEqualityComparer<>Anahtar için bir aşırı yükleme eklendi .

Sınıf:

public static class Equality<T>
{
    public static IEqualityComparer<T> CreateComparer<V>(Func<T, V> keySelector)
    {
        return CreateComparer(keySelector, null);
    }

    public static IEqualityComparer<T> CreateComparer<V>(Func<T, V> keySelector, 
                                                         IEqualityComparer<V> comparer)
    {
        return new KeyEqualityComparer<V>(keySelector, comparer);
    }

    class KeyEqualityComparer<V> : IEqualityComparer<T>
    {
        readonly Func<T, V> keySelector;
        readonly IEqualityComparer<V> comparer;

        public KeyEqualityComparer(Func<T, V> keySelector, 
                                   IEqualityComparer<V> comparer)
        {
            if (keySelector == null)
                throw new ArgumentNullException("keySelector");

            this.keySelector = keySelector;
            this.comparer = comparer ?? EqualityComparer<V>.Default;
        }

        public bool Equals(T x, T y)
        {
            return comparer.Equals(keySelector(x), keySelector(y));
        }

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

bunu şu şekilde kullanabilirsiniz:

var comparer1 = Equality<Person>.CreateComparer(p => p.ID);
var comparer2 = Equality<Person>.CreateComparer(p => p.Name);
var comparer3 = Equality<Person>.CreateComparer(p => p.Birthday.Year);
var comparer4 = Equality<Person>.CreateComparer(p => p.Name, StringComparer.CurrentCultureIgnoreCase);

Kişi basit bir sınıftır:

class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
    public DateTime Birthday { get; set; }
}

3
Anahtar için bir karşılaştırıcı sağlamanıza olanak tanıyan bir uygulama sağlamak için +1. Bu, daha fazla esneklik sağlamanın yanı sıra, hem karşılaştırmalar hem de karma için kutulama değeri türlerini önler.
devgeezer

2
Bu, buradaki en ayrıntılı cevaptır. Ayrıca bir boş onay ekledim. Tamamlayınız.
nawfal

11
public class FuncEqualityComparer<T> : IEqualityComparer<T>
{
    readonly Func<T, T, bool> _comparer;
    readonly Func<T, int> _hash;

    public FuncEqualityComparer( Func<T, T, bool> comparer )
        : this( comparer, t => t.GetHashCode())
    {
    }

    public FuncEqualityComparer( Func<T, T, bool> comparer, Func<T, int> hash )
    {
        _comparer = comparer;
        _hash = hash;
    }

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

    public int GetHashCode( T obj )
    {
        return _hash( obj );
    }
}

Uzantılarla: -

public static class SequenceExtensions
{
    public static bool SequenceEqual<T>( this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> comparer )
    {
        return first.SequenceEqual( second, new FuncEqualityComparer<T>( comparer ) );
    }

    public static bool SequenceEqual<T>( this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> comparer, Func<T, int> hash )
    {
        return first.SequenceEqual( second, new FuncEqualityComparer<T>( comparer, hash ) );
    }
}

@Sam (bu yorum itibariyle artık var olmayan): Kodu bir düzenleme davranışı olmadan temizledi (ve + 1'ledi). Stackoverflow.com/questions/98033/…
Ruben Bartelink

6

orip'in cevabı harika.

İşte daha da kolaylaştırmak için küçük bir uzatma yöntemi:

public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list, Func<T, object>    keyExtractor)
{
    return list.Distinct(new KeyEqualityComparer<T>(keyExtractor));
}
var distinct = foo.Distinct(x => x.ToLower())

2

Kendi sorumu cevaplayacağım. Sözlükleri setler olarak ele almak için en basit yöntem, dict.Keys'e set işlemlerini uygulamak, ardından Enumerable.ToDictionary (...) ile Sözlüklere geri dönüştürmek gibi görünüyor.


2

IEqualityCompare ile lambda ifadesinin uygulanması, null değerlere önem verir ve IEqualityComparer oluşturmak için uzantı yöntemlerini kullanır.

Bir Linq sendikasında bir IEqualityComparer oluşturmak için tek yapmanız gereken

persons1.Union(persons2, person => person.LastName)

Karşılaştırıcı:

public class LambdaEqualityComparer<TSource, TComparable> : IEqualityComparer<TSource>
{
  Func<TSource, TComparable> _keyGetter;

  public LambdaEqualityComparer(Func<TSource, TComparable> keyGetter)
  {
    _keyGetter = keyGetter;
  }

  public bool Equals(TSource x, TSource y)
  {
    if (x == null || y == null) return (x == null && y == null);
    return object.Equals(_keyGetter(x), _keyGetter(y));
  }

  public int GetHashCode(TSource obj)
  {
    if (obj == null) return int.MinValue;
    var k = _keyGetter(obj);
    if (k == null) return int.MaxValue;
    return k.GetHashCode();
  }
}

Ayrıca tür çıkarımını desteklemek için bir uzantı yöntemi eklemeniz gerekir

public static class LambdaEqualityComparer
{
       // source1.Union(source2, lambda)
        public static IEnumerable<TSource> Union<TSource, TComparable>(
           this IEnumerable<TSource> source1, 
           IEnumerable<TSource> source2, 
            Func<TSource, TComparable> keySelector)
        {
            return source1.Union(source2, 
               new LambdaEqualityComparer<TSource, TComparable>(keySelector));
       }
   }

1

Tek bir optimizasyon: Kullanıma hazır EqualityComparer'ı, yetkilendirmek yerine değer karşılaştırmaları için kullanabiliriz.

Bu, gerçek karşılaştırma mantığı artık zaten aşırı yüklemiş olabileceğiniz GetHashCode () ve Equals () içinde kaldığından uygulamayı daha temiz hale getirir.

İşte kod:

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

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

Nesnenizde GetHashCode () ve Equals () yöntemlerini aşırı yüklemeyi unutmayın.

Bu gönderi bana yardımcı oldu: c # iki genel değeri karşılaştır

Suşil


1
Üzerinde yorumda tanımlanan NB aynı sorun stackoverflow.com/questions/98033/... - obj.GetHashCode () farz cant mantıklı
Ruben Bartelink

4
Bunun amacını anlamıyorum. Varsayılan eşitlik karşılaştırıcısına eşdeğer bir eşitlik karşılaştırıcısı oluşturdunuz. Öyleyse neden doğrudan kullanmıyorsun?
CodesInChaos

1

orip'in cevabı harika. Orip'in cevabını genişletmek için:

Çözümün anahtarının "anonim türü" aktarmak için "Uzantı Yöntemi" ni kullanmak olduğunu düşünüyorum.

    public static class Comparer 
    {
      public static IEqualityComparer<T> CreateComparerForElements<T>(this IEnumerable<T> enumerable, Func<T, object> keyExtractor)
      {
        return new KeyEqualityComparer<T>(keyExtractor);
      }
    }

Kullanımı:

var n = ItemList.Select(s => new { s.Vchr, s.Id, s.Ctr, s.Vendor, s.Description, s.Invoice }).ToList();
n.AddRange(OtherList.Select(s => new { s.Vchr, s.Id, s.Ctr, s.Vendor, s.Description, s.Invoice }).ToList(););
n = n.Distinct(x=>new{Vchr=x.Vchr,Id=x.Id}).ToList();

0
public static Dictionary<TKey, TValue> Distinct<TKey, TValue>(this IEnumerable<TValue> items, Func<TValue, TKey> selector)
  {
     Dictionary<TKey, TValue> result = null;
     ICollection collection = items as ICollection;
     if (collection != null)
        result = new Dictionary<TKey, TValue>(collection.Count);
     else
        result = new Dictionary<TKey, TValue>();
     foreach (TValue item in items)
        result[selector(item)] = item;
     return result;
  }

Bu, lambda içeren bir özelliği şu şekilde seçmeyi mümkün kılar: .Select(y => y.Article).Distinct(x => x.ArticleID);


-2

Mevcut bir sınıf bilmiyorum ama şöyle bir şey:

public class MyComparer<T> : IEqualityComparer<T>
{
  private Func<T, T, bool> _compare;
  MyComparer(Func<T, T, bool> compare)
  {
    _compare = compare;
  }

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

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

Not: Bunu henüz derlemedim ve çalıştırmadım, bu yüzden bir yazım hatası veya başka bir hata olabilir.


1
Üzerinde yorumda tanımlanan NB aynı sorun stackoverflow.com/questions/98033/... - obj.GetHashCode () farz cant mantıklı
Ruben Bartelink
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.