SORU
18 EYLÜL 2008, PERŞEMBE


Wrap bir IEqualityComparer temsilci

Birkaç Etmeniz.Sayısız işlevleri IEqualityComparer<T> al. delegate(T,T)=>bool IEqualityComparer<T> hayata uyum sağlayan uygun kapsayıcı bir sınıf var mı? Kolay yazmak için yeterli eğer doğru bir hashcode tanımlama ile ilgili sorunlar göz ardı ederseniz () ama eğer out-of-the-box bir çözüm varsa bilmek isterim.

Özellikle, Dictionarys üzerinde küme işlemleri üyelik, farklı kurallara göre değerlerini koruyarak () tanımlamak için sadece Tuşlarını kullanarak yapmak istiyorum.

CEVAP
15 EYLÜL 2010, ÇARŞAMBA


GetHashCode önemi üzerinde

Diğerleri zaten herhangi bir özel IEqualityComparer<T> uygulama gerçeğini yorumladıgerçekten GetHashCode bir yöntem içermelidir; ama kimse bir açıklama yapmadığınınedenherhangi bir ayrıntı.

İşte bu yüzden. Sorunuzu özellikle SERİ uzatma yöntemleri; yaklaşık bahsedertümbu hash kodları düzgün çalışması için kullanır, onlar içten verimliliği için karma tablo kullanmak için.

Örneğin Distinct. Eğer kullanılan tüm Equals bir yöntemi olsaydı bu uzantı yöntemi etkilerini göz önünde bulundurun. Nasıl bir öğe zaten eğer sadece Equals eğer bir sıra ile taranmış olup olmadığını mı? Çoktan baktım ve bir maç için kontrol değerleri tüm koleksiyonu numaralandırma. Bu kötü durum O(N . kullanarak Distinct neden olur ^sup>2) (N) (O) yerine algoritma!

Neyse ki, bu durum böyle değil. Distinct gelmezsadeceEquals; GetHashCode de kullanır. Aslındakesinlikledeğildüzgün bir doğru malzemeler IEqualityComparer<T> GetHashCode olmadan bir iş. Aşağıda yapmacık bir örnek bu fotoğraf.

Demek şu tipi var:

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 List<Value> bir ben var ve farklı bir ad ile tüm öğeleri bulmak istiyorum diyelim. Bu Distinct için mükemmel bir senaryo, özel bir eşitlik karşılaştırıcısı kullanarak. Hadi Aku's answer Comparer<T> sınıfı kullanın:

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

Eğer Name aynı özelliği Value öğeleri bir sürü var şimdi, Eğer bir değer Distinct, sağ tarafından döndürülen içine çökmesi lazım? 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);
}

Çıkış:

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

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

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

Çıkış:

[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

Tekrar: bir işe yaramadı.

Eğer düşünürsen, gayet mantıklı Distinct kullanmak HashSet<T> (ya da eşdeğer) dahili ve GroupBy kullanmak gibi bir şey Dictionary<TKey, List<T>> DAHİLİ olarak. Bu yöntemler işe yaramazsa, neden bu açıklayabilir misiniz? Bunu deneyelim:

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

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

Çıkış:

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

Evet... her şey anlam kazanmaya başladı?

Umarım bu örnekler: GetHashCode IEqualityComparer<T> herhangi uygun bir uygulama da dahil olmak üzere neden bu kadar önemli olduğunu açık.


Orijinal cevap

orip's answer genişletme:

Burada yapılabilecek iyileştirmeler bir çift vardır.

  1. Önce Func<T, TKey> yerine Func<T, object>; keyExtractor gerçek başlı başına değer tipi anahtarları boks önleyecek bir alırdım.
  2. İkinci, istiyorum aslında ekleyin where TKey : IEquatable<TKey> kısıtlama; bu-ecek önlemek boks Equals arama (object.Equals alır object parametre; gerekir IEquatable<TKey> uygulama için bir TKey parametre olmadan boks). Açıkçası bu çok ciddi bir kısıtlama teşkil edebilir, kısıtlama olmadan, temel bir sınıf ve türetilmiş bir sınıf yapabilir.

Ortaya çıkan kod gibi 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));
    }
}

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Matt Harding

    Matt Harding

    23 Mayıs 2006
  • pjtoohot

    pjtoohot

    15 NİSAN 2008
  • xCraash

    xCraash

    6 Temmuz 2012