Bungkus delegasi dalam IEqualityComparer

127

Beberapa fungsi Linq. Dapat dihitung IEqualityComparer<T>. Apakah ada kelas pembungkus yang nyaman yang mengadaptasi delegate(T,T)=>booluntuk mengimplementasikan IEqualityComparer<T>? Cukup mudah untuk menulis satu (jika Anda mengabaikan masalah dengan mendefinisikan kode hash yang benar), tetapi saya ingin tahu apakah ada solusi out-of-the-box.

Secara khusus, saya ingin melakukan operasi set pada Dictionarys, hanya menggunakan Kunci untuk menentukan keanggotaan (sambil mempertahankan nilai-nilai sesuai dengan aturan yang berbeda).

Marcelo Cantos
sumber

Jawaban:

44

Biasanya, saya akan menyelesaikan ini dengan mengomentari @Sam pada jawabannya (Saya telah melakukan beberapa pengeditan pada posting asli untuk membersihkannya sedikit tanpa mengubah perilaku.)

Berikut ini adalah jawaban saya terhadap @ Sam , dengan perbaikan kritis [IMNSHO] ke kebijakan hashing default: -

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 );
    }
}
Ruben Bartelink
sumber
5
Sejauh yang saya ketahui, ini adalah jawaban yang benar . Apa pun IEqualityComparer<T>yang GetHashCodekeluar akan langsung rusak.
Dan Tao
1
@Joshua Frank: Tidak sah untuk menggunakan kesetaraan hash untuk menyiratkan kesetaraan - hanya kebalikannya yang benar. Singkatnya, @Dan Tao sepenuhnya benar dalam apa yang dia katakan, dan jawaban ini hanyalah penerapan fakta ini untuk jawaban yang sebelumnya tidak lengkap
Ruben Bartelink
2
@Ruben Bartelink: Terima kasih telah mengklarifikasi. Tapi saya masih tidak mengerti kebijakan hashing Anda dari t => 0. Jika semua objek selalu hash ke hal yang sama (nol), bukankah itu bahkan lebih rusak daripada menggunakan obj.GetHashCode, per titik @Dan Tao? Mengapa tidak selalu memaksa penelepon untuk menyediakan fungsi hash yang baik?
Joshua Frank
1
Dengan demikian tidak masuk akal untuk mengasumsikan bahwa algoritma arbitrer dalam suatu Func yang telah disediakan tidak mungkin mengembalikan true meskipun kode hash berbeda. Maksud Anda bahwa mengembalikan nol setiap saat bukanlah hashing adalah benar. Itu sebabnya ada kelebihan yang mengambil fungsi hashing ketika profiler memberi tahu kami bahwa pencarian tidak cukup efisien. Satu-satunya titik dalam semua ini adalah bahwa jika Anda akan memiliki algoritma hashing default, itu harus yang bekerja 100% dari waktu dan tidak memiliki perilaku yang benar secara dangkal yang berbahaya. Dan kemudian kita dapat bekerja pada kinerja!
Ruben Bartelink
4
Dengan kata lain, karena Anda menggunakan pembanding kustom itu tidak ada hubungannya dengan kode hash default objek yang terkait dengan pembanding default , sehingga Anda tidak dapat menggunakannya.
Peet Brits
170

Tentang pentingnya GetHashCode

Yang lain sudah berkomentar tentang fakta bahwa IEqualityComparer<T>implementasi kustom harus benar-benar menyertakan GetHashCodemetode ; tapi tidak ada yang mau repot-repot menjelaskan alasannya .

Inilah sebabnya. Pertanyaan Anda secara khusus menyebutkan metode ekstensi LINQ; hampir semua ini mengandalkan kode hash untuk bekerja dengan baik, karena mereka menggunakan tabel hash secara internal untuk efisiensi.

Ambil Distinctcontohnya. Pertimbangkan implikasi metode ekstensi ini jika semua yang digunakan adalah Equalsmetode. Bagaimana Anda menentukan apakah suatu item sudah dipindai secara berurutan jika hanya dimiliki Equals? Anda menghitung seluruh koleksi nilai yang telah Anda lihat dan memeriksa kecocokan. Ini akan menghasilkan Distinctpenggunaan algoritma O (N 2 ) kasus terburuk alih-alih O (N)!

Untungnya, ini bukan masalahnya. Distincttidak hanya digunakan Equals; itu menggunakan GetHashCodejuga. Bahkan, itu benar - benar tidak berfungsi sebagaimana mestinya tanpa IEqualityComparer<T>pasokan yang tepatGetHashCode . Di bawah ini adalah contoh buat menggambarkan ini.

Katakanlah saya memiliki tipe berikut:

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

Sekarang katakan saya punya List<Value>dan saya ingin menemukan semua elemen dengan nama yang berbeda. Ini adalah kasus penggunaan yang sempurna untuk Distinctmenggunakan pembanding kesetaraan khusus. Jadi mari kita gunakan Comparer<T>kelas dari jawaban Aku :

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

Sekarang, jika kita memiliki banyak Valueelemen dengan Nameproperti yang sama , semuanya harus runtuh menjadi satu nilai yang dikembalikan Distinct, kan? Ayo lihat...

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

Keluaran:

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

Hmm, itu tidak berhasil, kan?

Bagaimana dengan GroupBy? Mari kita coba itu:

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

Keluaran:

[KEY = 'x: 1346013431']
x: 1346013431
[KEY = '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

Lagi: tidak berhasil.

Jika Anda memikirkannya, masuk akal untuk Distinctmenggunakan HashSet<T>(atau setara) secara internal, dan untuk GroupBymenggunakan sesuatu seperti Dictionary<TKey, List<T>>internal. Bisakah ini menjelaskan mengapa metode ini tidak berhasil? Mari kita coba ini:

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

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

Keluaran:

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

Ya ... mulai masuk akal?

Semoga dari contoh-contoh ini jelas mengapa memasukkan yang sesuai GetHashCodedalam IEqualityComparer<T>implementasi apa pun begitu penting.


Jawaban asli

Memperluas jawaban orip :

Ada beberapa perbaikan yang bisa dilakukan di sini.

  1. Pertama, saya akan mengambil Func<T, TKey>alih-alih Func<T, object>; ini akan mencegah tinju kunci tipe nilai dalam keyExtractordirinya sendiri.
  2. Kedua, saya sebenarnya akan menambahkan where TKey : IEquatable<TKey>batasan; ini akan mencegah tinju dalam Equalspanggilan ( object.Equalsmengambil objectparameter; Anda perlu IEquatable<TKey>implementasi untuk mengambil TKeyparameter tanpa tinju). Jelas ini bisa menimbulkan batasan yang terlalu berat, sehingga Anda bisa membuat kelas dasar tanpa batasan dan kelas turunan dengannya.

Seperti apa bentuk kode yang dihasilkan:

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));
    }
}
Dan Tao
sumber
1
StrictKeyEqualityComparer.EqualsMetode Anda tampaknya sama dengan KeyEqualityComparer.Equals. Apakah TKey : IEquatable<TKey>kendala membuat TKey.Equalspekerjaan berbeda?
Justin Morgan
2
@JustinMorgan: Ya - dalam kasus pertama, karena TKeymungkin jenis sembarang, kompiler akan menggunakan metode virtual Object.Equalsyang akan memerlukan tinju parameter tipe nilai, misalnya int,. Dalam kasus terakhir, bagaimanapun, karena TKeydibatasi untuk mengimplementasikan IEquatable<TKey>, TKey.Equalsmetode akan digunakan yang tidak memerlukan tinju.
Dan Tao
2
Sangat menarik, terima kasih atas informasinya. Saya tidak tahu GetHashCode memiliki implikasi LINQ ini sampai melihat jawaban ini. Sangat bagus untuk digunakan di masa depan.
Justin Morgan
1
@JohannesH: Mungkin! Akan menghilangkan kebutuhan StringKeyEqualityComparer<T, TKey>juga.
Dan Tao
1
+1 @DanTao: Terlambat terima kasih atas penjelasan yang bagus tentang mengapa seseorang tidak boleh mengabaikan kode hash saat mendefinisikan kesetaraan di .Net.
Marcelo Cantos
118

Saat Anda ingin menyesuaikan pengecekan kesetaraan, 99% waktu Anda tertarik untuk mendefinisikan kunci untuk dibandingkan, bukan perbandingan itu sendiri.

Ini bisa menjadi solusi yang elegan (konsep dari metode semacam daftar Python ).

Pemakaian:

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

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

The KeyEqualityComparerkelas:

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();
    }
}
orip
sumber
3
Ini jauh lebih baik daripada jawaban aku.
SLaks
Jelas pendekatan yang tepat. Ada beberapa perbaikan yang dapat dilakukan, menurut pendapat saya, yang telah saya sebutkan dalam jawaban saya sendiri.
Dan Tao
1
Ini adalah kode yang sangat elegan, tetapi tidak menjawab pertanyaan, itulah sebabnya saya menerima jawaban @ aku. Saya ingin pembungkus untuk Func <T, T, bool> dan saya tidak perlu mengekstraksi kunci, karena kunci sudah dipisahkan dalam Kamus saya.
Marcelo Cantos
6
@ Marscelo: Tidak apa-apa, Anda bisa melakukannya; tetapi perlu diketahui bahwa jika Anda akan mengambil pendekatan @ aku, Anda benar-benar harus menambahkan Func<T, int>untuk memasok kode hash untuk Tnilai (seperti yang telah disarankan dalam, misalnya, jawaban Ruben ). Kalau tidak, IEqualityComparer<T>implementasi yang tersisa cukup rusak, terutama yang berkaitan dengan kegunaannya dalam metode ekstensi LINQ. Lihat jawaban saya untuk diskusi tentang mengapa ini.
Dan Tao
Ini bagus tetapi jika kunci yang dipilih adalah tipe nilai akan ada tinju yang tidak perlu. Mungkin akan lebih baik untuk memiliki TKey untuk mendefinisikan kunci.
Graham Ambrose
48

Saya khawatir tidak ada bungkus seperti itu di luar kotak. Namun tidak sulit untuk membuatnya:

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));
aku
sumber
1
Namun, berhati-hatilah dengan implementasi GetHashCode itu. Jika Anda benar-benar akan menggunakannya dalam semacam tabel hash Anda akan menginginkan sesuatu yang sedikit lebih kuat.
thecoop
46
kode ini memiliki masalah serius! mudah untuk membuat kelas yang memiliki dua objek yang sama dalam hal pembanding ini tetapi memiliki kode hash yang berbeda.
empi
10
Untuk memperbaiki ini, kelas membutuhkan anggota lain private readonly Func<T, int> _hashCodeResolveryang juga harus dilewatkan di konstruktor dan digunakan dalam GetHashCode(...)metode.
herzmeister
6
Saya ingin tahu: Mengapa Anda menggunakan obj.ToString().ToLower().GetHashCode()bukan obj.GetHashCode()?
Justin Morgan
3
Tempat-tempat dalam kerangka kerja yang IEqualityComparer<T>selalu menggunakan hashing di belakang layar (misalnya, GroupBy, Distinct, Kecuali, Bergabung, dll LINQ's) dan kontrak MS tentang hashing rusak dalam implementasi ini. Berikut kutipan dokumentasi MS: "Implementasi diperlukan untuk memastikan bahwa jika metode Persamaan mengembalikan nilai true untuk dua objek x dan y, maka nilai yang dikembalikan oleh metode GetHashCode untuk x harus sama dengan nilai yang dikembalikan untuk y." Lihat: msdn.microsoft.com/en-us/library/ms132155
devgeezer
22

Sama seperti jawaban Dan Tao, tetapi dengan beberapa peningkatan:

  1. Mengandalkan EqualityComparer<>.Defaultmelakukan perbandingan aktual sehingga menghindari tinju untuk jenis nilai structyang telah diterapkan IEquatable<>.

  2. Sejak EqualityComparer<>.Defaultdigunakan itu tidak meledak null.Equals(something).

  3. Disediakan pembungkus statis di sekitar IEqualityComparer<>yang akan memiliki metode statis untuk membuat instance dari pembanding - memudahkan panggilan. Membandingkan

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

    dengan

    new EqualityComparer<Person, int>(p => p.ID);
  4. Menambahkan kelebihan untuk menentukan IEqualityComparer<>kunci.

Kelas:

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

Anda dapat menggunakannya seperti ini:

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

Orang adalah kelas sederhana:

class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
    public DateTime Birthday { get; set; }
}
ldp615
sumber
3
+1 untuk menyediakan implementasi yang memungkinkan Anda memberikan pembanding untuk kunci tersebut. Selain memberikan lebih banyak fleksibilitas, ini juga menghindari tipe nilai tinju untuk perbandingan dan hashing.
devgeezer
2
Ini adalah jawaban yang paling sempurna di sini. Saya menambahkan cek nol juga. Lengkap.
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 );
    }
}

Dengan ekstensi: -

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 ) );
    }
}
Ruben Bartelink
sumber
@ Sam (yang tidak lagi ada pada komentar ini): Membersihkan kode tanpa perilaku yang tidak adil (dan +1). Menambahkan Riff di stackoverflow.com/questions/98033/…
Ruben Bartelink
6

jawaban orip itu bagus.

Berikut sedikit metode ekstensi untuk membuatnya lebih mudah:

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())
Bruno
sumber
2

Saya akan menjawab pertanyaan saya sendiri. Untuk memperlakukan Kamus sebagai set, metode paling sederhana tampaknya menerapkan operasi set ke dict.Keys, lalu konversikan kembali ke Kamus dengan Enumerable.ToDictionary (...).

Marcelo Cantos
sumber
2

Implementasi di (teks Jerman) Menerapkan IEqualityCompare dengan ekspresi lambda peduli tentang nilai nol dan menggunakan metode ekstensi untuk menghasilkan IEqualityComparer.

Untuk membuat IEqualityComparer dalam serikat Linq, Anda hanya perlu menulis

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

Pembanding:

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

Anda juga perlu menambahkan metode ekstensi untuk mendukung inferensi tipe

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));
       }
   }
Goreng
sumber
1

Hanya satu pengoptimalan: Kita dapat menggunakan EqualityComparer out-of-the-box untuk perbandingan nilai, daripada mendelegasikannya.

Ini juga akan membuat implementasi lebih bersih karena logika perbandingan aktual sekarang berada di GetHashCode () dan Equals () yang mungkin sudah Anda overload.

Ini kodenya:

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

Jangan lupa untuk membebani metode GetHashCode () dan Equals () pada objek Anda.

Posting ini membantu saya: c # membandingkan dua nilai generik

Sushil

Sushil
sumber
1
NB masalah yang sama seperti yang diidentifikasi dalam komentar di stackoverflow.com/questions/98033/... - CANT menganggap obj.GetHashCode () masuk akal
Ruben Bartelink
4
Saya tidak mendapatkan tujuan yang satu ini. Anda membuat pembanding kesetaraan yang setara dengan pembanding kesetaraan default. Jadi mengapa Anda tidak menggunakannya secara langsung?
CodesInChaos
1

jawaban orip itu bagus. Memperluas jawaban orip:

saya pikir kunci solusinya adalah menggunakan "Metode Perpanjangan" untuk mentransfer "jenis anonim".

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

Pemakaian:

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

Ini memungkinkan untuk memilih properti dengan lambda seperti ini: .Select(y => y.Article).Distinct(x => x.ArticleID);

Maks
sumber
-2

Saya tidak tahu kelas yang ada tetapi sesuatu seperti:

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

Catatan: Saya belum mengkompilasi dan menjalankan ini, jadi mungkin ada kesalahan ketik atau bug lainnya.

Gregg
sumber
1
NB masalah yang sama seperti yang diidentifikasi dalam komentar di stackoverflow.com/questions/98033/... - CANT menganggap obj.GetHashCode () masuk akal
Ruben Bartelink