IEqualityComparer <T> ve IEquatable <T> arasındaki fark nedir?


151

Nerede IEqualityComparer<T>ve IEquatable<T>kullanılması gereken senaryoları anlamak istiyorum . Her ikisi için de MSDN belgeleri çok benzer.


1
MSDN sez: "Bu arabirim, koleksiyonlar için özelleştirilmiş eşitlik karşılaştırmasının uygulanmasına izin verir . " MSDN ayrıca EqualityComparer<T>" EqualityComparer<T>eşitliği kullanarak testlerIEquatable<T>
radarbob

... yukarıdaki herhangi bir Tuygulama için özel bir koleksiyon oluşturmak gerektiğini göstermektedir IEquatable<T>. Böyle bir koleksiyonda bir List<T>tür ince böcek var mı?
radarbob


@RamilShavaleev bağlantı koptu
ChessMax

Yanıtlar:


117

IEqualityComparer<T>, türdeki iki nesne üzerinde karşılaştırma yapan bir nesnenin arabirimidir T.

IEquatable<T>türünde bir nesne içindir, Tböylece kendisini aynı türden bir başkasıyla karşılaştırabilir.


1
Aynı fark IComparable / IComparer
boctulus

3
Kaptan Obvious
Stepan Ivanenko

(Edited) IEqualityComparer<T>bir nesne için bir arayüz (bir hafif sınıfı farklı genellikle T) üzerinde çalışır karşılaştırma fonksiyonları sağlarT
rwong

61

IEquatable<T>Ya da kullanıp kullanmayacağınıza karar verirken IEqualityComparer<T>:

TEşitlik için iki örneği test etmenin tercih edilen bir yolu var mı veya eşit derecede geçerli birkaç yol var mı?

  • TEşitlik için iki örneği test etmenin tek bir yolu varsa veya birkaç yöntemden biri tercih edilirse IEquatable<T>, doğru seçim olacaktır: Bu arabirimin yalnızca Tkendi başına uygulanması gerekir, böylece bir örneğinin Tdahili bilgisi vardır. kendini başka bir örneğiyle nasıl karşılaştırabilirim T.

  • Öte yandan, Teşitlik için iki s'yi karşılaştırmak için eşit derecede makul birkaç yöntem varsa , IEqualityComparer<T>daha uygun görünecektir: Bu arayüz Tkendi başına değil, diğer "dış" sınıflar tarafından uygulanmaya yöneliktir . Bu nedenle, Teşitlik için iki örneği test ederken , Tiçsel eşitlik anlayışına sahip olmadığından IEqualityComparer<T>, testi belirli gereksinimlerinize göre gerçekleştiren bir örneği açıkça seçmeniz gerekecektir .

Misal:

Bu iki türü ele alalım ( değer semantiğine sahip olması gerekir ):

interface IIntPoint : IEquatable<IIntPoint>
{
    int X { get; }
    int Y { get; }
}

interface IDoublePoint  // does not inherit IEquatable<IDoublePoint>; see below.
{
    double X { get; }
    double Y { get; }
}

Neden bu tiplerden sadece biri miras alır? IEquatable<> , diğeri mi?

Teorik olarak, her iki türden iki örneği karşılaştırmanın yalnızca bir mantıklı yolu vardır: Her iki örnekte de Xve Yözellikleri eşitse bunlar eşittir. Bu düşünceye göre, her iki tip de uygulanmalıdır IEquatable<>, çünkü bir eşitlik testi yapmanın başka anlamlı yolları olduğu görülmemektedir.

Buradaki sorun, eşitlik için kayan nokta sayılarının karşılaştırılmasının, dakika yuvarlama hataları nedeniyle beklendiği gibi çalışmayabileceğidir . Eşitlik için kayan nokta sayılarını karşılaştırmak için her biri belirli avantajlara ve değiş tokuşlara sahip farklı yöntemler vardır ve kendinize hangi yöntemin uygun olduğunu seçebilirsiniz.

sealed class DoublePointNearEqualityComparerByTolerance : IEqualityComparer<IDoublePoint>
{
    public DoublePointNearEqualityComparerByTolerance(double tolerance) {  }
    
    public bool Equals(IDoublePoint a, IDoublePoint b)
    {
        return Math.Abs(a.X - b.X) <= tolerance  &&  Math.Abs(a.Y - b.Y) <= tolerance;
    }
    
}

Bağlandığım sayfanın (yukarıda) açık bir şekilde, eşitliğe yakınlık için yapılan bu testin bazı zayıflıkları olduğunu açıkça belirtti. Bu bir IEqualityComparer<T>uygulama olduğundan, amaçlarınız için yeterince iyi değilse kolayca değiştirebilirsiniz.


6
Meşru bir yana çift noktalı eşitlik test etmek için önerilen karşılaştırma yöntemi bozuldu, IEqualityComparer<T>uygulama gerekir içinde gerektiği anlamına gelen bir eşdeğerlik ilişkisi uygulamak her iki nesne, üçte birine eşit olması karşılaştırın durumlarda, bunlar gereken birbirlerine göre eş. Yukarıdakilere aykırı IEquatable<T>veya herhangi IEqualityComparer<T>bir şekilde uygulanan herhangi bir sınıf kırılır.
supercat

5
Daha iyi bir örnek, dizeler arasındaki karşılaştırmalardır. Dizeleri yalnızca aynı bayt dizisini içeriyorlarsa eşit olarak gören bir dize karşılaştırma yöntemine sahip olmak mantıklıdır, ancak büyük / küçük harfe duyarlı olmayan karşılaştırmalar gibi denklik ilişkilerini de oluşturan başka yararlı karşılaştırma yöntemleri de vardır . Not Bir o IEqualityComparer<String>"Merhaba" olarak gören "Hello" "Merhaba" ve düşünün birbirine eşit olmalıdır hem "Merhaba" ve "Hello" eşit, ancak çoğu karşılaştırma yöntemleri için bir sorun olmaz.
Supercat

@supercat, değerli geri bildiriminiz için teşekkürler. Önümüzdeki birkaç gün içinde cevabımı revize etmem muhtemeldir, bu yüzden lütfen uygun gördüğünüzde düzenleme yapmaktan çekinmeyin.
stakx - artık

Tam da ihtiyacım olan şey ve yaptığım şey bu. Ancak, değerler farklı olduğunda false döndüreceği GetHashCode'u geçersiz kılmaya ihtiyaç vardır. GetHashCode'unuzu nasıl ele alırsınız?
teapeng

@teapeng: Hangi spesifik kullanım durumundan bahsettiğinizden emin değilim. Genel olarak, GetHashCodeiki değerin farklı olup olmadığını hızlı bir şekilde belirlemenizi sağlar. Kurallar kabaca aşağıdaki gibidir: (1) GetHashCode her zaman aynı değer için aynı karma kodunu üretmelidir. (2) GetHashCode hızlı (daha hızlı ) olmalıdır Equals. (3) GetHashCode kesin olmak zorunda değildir (o kadar kesin değildir Equals). Bu, farklı değerler için aynı karma kodunu üretebileceği anlamına gelir. Ne kadar hassas yaparsanız o kadar iyi olur, ancak hızlı tutmak muhtemelen daha önemlidir.
stakx - artık

27

Zaten ne olduklarının temel tanımına zaten sahipsiniz . Uygulamak Kısacası, IEquatable<T>sınıf T, Equalstip bir nesne üzerinde bir yöntem Tnesnenin kendisi (eşitlik test edilen bir) aynı türde başka bir örneğine eşit olup olmadığını bildirir T. Halbuki, tipik olarak, kapsamının dışında kalan IEqualityComparer<T>herhangi iki örneğinin eşitliğini sınamak içindir .TT

Gelince ne oldukları için ilk başta kafa karıştırıcı olabilir. Tanımdan, bu nedenle IEquatable<T>(sınıfın Tkendisinde tanımlanan ), nesnelerinin / örneklerinin benzersizliğini temsil etmek için fiili standart olması gerektiği açık olmalıdır. HashSet<T>, Dictionary<T, U>(Düşünüyor GetHashCode, hem de geçersiz kılınır) Containsüzerine List<T>bunun vb yapmak kullanımı. Uygulama IEqualityComparer<T>üzerinde Tyukarıda belirtilen genel durumları yardımcı olmuyor. Daha sonra, IEquatable<T>başka herhangi bir sınıfa uygulamak için çok az değer vardır T. Bu:

class MyClass : IEquatable<T>

nadiren mantıklı.

Diğer yandan

class T : IEquatable<T>
{
    //override ==, !=, GetHashCode and non generic Equals as well

    public bool Equals(T other)
    {
        //....
    }
}

nasıl yapılması gerektiğidir.

IEqualityComparer<T>genel bir kural olarak değil, eşitliğin özel olarak onaylanmasını istediğinizde yararlı olabilir. Örneğin, bir Personnoktada bir sınıfta iki kişinin yaşlarına göre eşitliğini test etmeniz gerekebilir. Bu durumda şunları yapabilirsiniz:

class Person
{
    public int Age;
}

class AgeEqualityTester : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Age == y.Age;
    }

    public int GetHashCode(Person obj)
    {
        return obj.Age.GetHashCode;
    }
}

Test etmek için deneyin

var people = new Person[] { new Person { age = 23 } };
Person p = new Person() { age = 23 };

print people.Contains(p); //false;
print people.Contains(p, new AgeEqualityTester()); //true

Benzer şekilde IEqualityComparer<T>üzerinde bir Tanlam ifade etmiyor.

class Person : IEqualityComparer<Person>

Doğru çalışıyor, ama gözler için iyi görünmüyor ve mantığı yendi.

Genellikle ihtiyacınız olan şey IEquatable<T>. Ayrıca ideal olarak sadece bir tane IEquatable<T>birdenIEqualityComparer<T> , farklı kriterlere göre mümkündür.

IEqualityComparer<T>Ve IEquatable<T>tam olarak benzer olan Comparer<T>ve IComparable<T>, karşılaştırma amacıyla yerine denk daha için kullanıldığı; burada aynı cevabı yazdığım iyi bir konu :)


public int GetHashCode(Person obj)dönmelidirobj.GetHashCode()
hyankov

@HristoYankov dönmeli obj.Age.GetHashCode. düzenleyecektir.
nawfal

Lütfen karşılaştırıcının neden nesnenin karma değerini karşılaştırdığı konusunda böyle bir varsayımda bulunması gerektiğini açıklayınız. Karşılaştırıcınız Kişinin Hash'ini nasıl hesapladığını bilmiyor, neden geçersiz kılsın?
hyankov

@HristoYankov Karşılaştırmanız Kişi'nin Hash - Tabii'yi nasıl hesapladığını bilmiyor , bu yüzden doğrudan person.GetHashCodehiçbir yere aramadık .... neden bunu geçersiz kıldınız ? - Geçersiz kılıyoruz çünkü tüm mesele IEqualityComparerkurallarımıza göre farklı bir karşılaştırma uygulamasına sahip olmak - gerçekten iyi bildiğimiz kurallar.
nawfal

Örneğimde, karşılaştırmamı Agemülke dayandırmam gerekiyor , bu yüzden çağırıyorum Age.GetHashCode. Yaş türü int, ancak türü ne olursa olsun .NET karma kod sözleşmesi olmasıdır different objects can have equal hash but equal objects cant ever have different hashes. Bu körü körüne inanabileceğimiz bir sözleşmedir. Elbette, özel karşılaştırıcılar da bu kurala uymalıdır. Eğer bir şey çağırmak bir someObject.GetHashCodeşeyleri kırarsa, o zaman bu tür uygulayıcıların sorunu someObject, bizim sorunumuz değil.
nawfal

11

IEqualityComparer, iki nesnenin eşitliği harici olarak uygulandığında, örneğin kaynağınız olmayan iki tür için bir karşılaştırıcı tanımlamak istiyorsanız veya iki şey arasındaki eşitliğin yalnızca sınırlı bir bağlamda anlamlı olduğu durumlarda kullanılır.

IEquatable nesnenin kendisi (eşitlik için karşılaştırılan) uygulamak içindir.


"Eşitliğe Takılmak" (harici kullanım) sorununu gerçekten dile getiren tek kişisiniz. artı 1.
Royi Namir

4

Biri iki Ts'yi karşılaştırır . Diğeri kendini diğer Ts ile karşılaştırabilir . Genellikle, ikisini birden değil, aynı anda yalnızca birini kullanmanız gerekir.

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.