IEqualityComparer <T> और IEquitable <T> में क्या अंतर है?


151

मैं परिदृश्यों जहां समझना चाहते हैं IEqualityComparer<T>और IEquatable<T>इस्तेमाल किया जाना चाहिए। दोनों के लिए MSDN प्रलेखन बहुत समान दिखता है।


1
MSDN sez: "यह इंटरफ़ेस संग्रह के लिए अनुकूलित समानता तुलना के कार्यान्वयन की अनुमति देता है " MSDN EqualityComparer<T>इंटरफ़ेस को लागू करने के बजाय इनहेरिट करने की भी सिफारिश करता है "क्योंकि EqualityComparer<T>परीक्षण समानता का उपयोग करते हुएIEquatable<T>
radarbob

... उपरोक्त सुझाव है कि मुझे किसी भी Tकार्यान्वयन के लिए एक कस्टम संग्रह बनाना चाहिए IEquatable<T>। एक संग्रह की तरह List<T>यह सूक्ष्म बग में किसी तरह का होगा अन्यथा?
रडारबॉब

अच्छा स्पष्टीकरण anotherchris.net/csharp/...
रामिल Shavaleev

@RamilShavaleev का लिंक टूटा है
शतरंजमैक्स

जवाबों:


117

IEqualityComparer<T>किसी ऑब्जेक्ट के लिए एक इंटरफ़ेस है जो टाइप के दो ऑब्जेक्ट्स पर तुलना करता है T

IEquatable<T>एक प्रकार की वस्तु के लिए है Tताकि वह खुद की तुलना उसी प्रकार के दूसरे से कर सके।


1
समान अंतर IComparable / IComparer
boctulus

3
कप्तान स्पष्ट
स्टीफन इवानेंको

(संपादित) IEqualityComparer<T>एक ऑब्जेक्ट के लिए एक इंटरफ़ेस है (जो आमतौर पर एक हल्के वर्ग से अलग होता है T) जो तुलनात्मक कार्य प्रदान करता हैT
16:22

61

यह तय करने के लिए कि क्या उपयोग करना है IEquatable<T>या IEqualityComparer<T>, कोई पूछ सकता है:

क्या Tसमानता के लिए दो उदाहरणों का परीक्षण करने का एक पसंदीदा तरीका है , या कई समान रूप से मान्य तरीके हैं?

  • यदि Tसमानता के लिए दो उदाहरणों का परीक्षण करने का केवल एक ही तरीका है , या यदि कई तरीकों में से एक को प्राथमिकता दी जाती है, तो IEquatable<T>सही विकल्प होगा: इस इंटरफ़ेस को केवल Tस्वयं द्वारा लागू किया जाना चाहिए , ताकि किसी एक Tको आंतरिक ज्ञान हो कैसे खुद की तुलना दूसरे उदाहरण से करें T

  • दूसरी ओर, अगर Tसमानता के लिए दो एस की तुलना करने के कई समान रूप से उचित तरीके हैं , IEqualityComparer<T>तो अधिक उपयुक्त लगेगा: यह इंटरफ़ेस Tस्वयं द्वारा लागू करने के लिए नहीं है, लेकिन अन्य "बाहरी" वर्गों द्वारा। इसलिए, Tसमानता के लिए दो उदाहरणों का परीक्षण करते समय , क्योंकि समानता Tकी कोई आंतरिक समझ नहीं है, आपको एक IEqualityComparer<T>उदाहरण का स्पष्ट विकल्प बनाना होगा जो आपकी विशिष्ट आवश्यकताओं के अनुसार परीक्षण करता है।

उदाहरण:

आइए इन दो प्रकारों पर विचार करें (जिन्हें मान शब्दार्थ माना जाता है ):

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

केवल इन प्रकारों में से एक क्यों होगा IEquatable<>, लेकिन दूसरा नहीं?

सिद्धांत रूप में, दोनों प्रकारों के दो उदाहरणों की तुलना करने का केवल एक समझदार तरीका है: वे समान हैं यदि दोनों उदाहरणों में गुण Xऔर Yगुण समान हैं। इस सोच के अनुसार, दोनों प्रकार को लागू करना चाहिए IEquatable<>, क्योंकि यह संभावना नहीं लगती कि समानता परीक्षण करने के अन्य सार्थक तरीके हैं।

यहाँ मुद्दा यह है कि समानता के लिए फ्लोटिंग-पॉइंट नंबरों की तुलना करना संभव नहीं है, जैसा कि मिनट गोल चक्कर त्रुटियों के कारण होता हैनिकट-समानता के लिए फ्लोटिंग-पॉइंट संख्याओं की तुलना करने के विभिन्न तरीके हैं , प्रत्येक विशिष्ट फायदे और व्यापार-नापसंद के साथ, और आप खुद को चुनने में सक्षम होना चाह सकते हैं कि कौन सी विधि उपयुक्त है।

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

ध्यान दें कि मैं (ऊपर) से जुड़ा हुआ पृष्ठ स्पष्ट रूप से बताता है कि निकट-समानता के लिए इस परीक्षण में कुछ कमजोरियां हैं। चूंकि यह एक IEqualityComparer<T>कार्यान्वयन है, आप बस इसे स्वैप कर सकते हैं यदि यह आपके उद्देश्यों के लिए पर्याप्त नहीं है।


6
डबल-पॉइंट समानता का परीक्षण करने के लिए सुझाई गई तुलना पद्धति टूट गई है, क्योंकि किसी भी वैध IEqualityComparer<T>कार्यान्वयन को एक समतुल्य संबंध को लागू करना चाहिए , जिसका अर्थ है कि सभी मामलों में जहां दो वस्तुएं एक तिहाई के बराबर की तुलना करती हैं , उन्हें एक दूसरे के बराबर तुलना करनी चाहिए । कोई भी वर्ग जो ऊपर के विपरीत IEquatable<T>या IEqualityComparer<T>एक फैशन को लागू करता है, टूट जाता है।
सुपरकैट

5
एक बेहतर उदाहरण तार के बीच तुलना होगी। यह समझ में आता है कि एक स्ट्रिंग तुलना पद्धति है जो स्ट्रिंग को केवल बाइट्स के समान अनुक्रम के रूप में समान मानती है, लेकिन अन्य उपयोगी तुलना विधियां भी हैं जो मामले-असंवेदनशील तुलनाओं जैसे तुल्यता संबंधों का गठन करती हैं । ध्यान दें कि IEqualityComparer<String>जो "हैलो" और "hElLo" दोनों के समान "हैलो" मानता है, उसे "Hello" और "hElLo" पर एक दूसरे के बराबर विचार करना चाहिए, लेकिन अधिकांश तुलनात्मक तरीकों के लिए जो समस्या नहीं होगी।
सुपरकाट

@supercat, बहुमूल्य प्रतिक्रिया के लिए धन्यवाद। यह संभावना है कि मैं अगले कुछ दिनों में अपने उत्तर को संशोधित करने के लिए चारों ओर नहीं पहुंचूंगा, इसलिए यदि आप चाहें, तो जैसा कि आप फिट देखते हैं, कृपया संपादित करने के लिए स्वतंत्र महसूस करें।
stakx -

यह वही है जो मुझे चाहिए था और मैं क्या कर रहा हूं। हालाँकि GetHashCode को ओवरराइड करने की आवश्यकता है, जिसमें मान अलग-अलग होने पर यह गलत वापस आ जाएगा। आप अपने GetHashCode को कैसे संभालते हैं?
teapeng

@ ट्रिप: निश्चित नहीं कि आप किस विशिष्ट उपयोग के मामले में बात कर रहे हैं। सामान्यतया, GetHashCodeआपको यह निर्धारित करने की अनुमति देना चाहिए कि क्या दो मान भिन्न हैं। नियम मोटे तौर पर इस प्रकार हैं: (1) GetHashCode हमेशा समान मूल्य के लिए समान हैश कोड का उत्पादन करना चाहिए। (2) GetHashCode तेज (से तेज ) होना चाहिए Equals(3) GetHashCode सटीक होना जरूरी नहीं है (के रूप में सटीक नहीं है Equals)। इसका मतलब है कि यह विभिन्न मूल्यों के लिए समान हैश कोड का उत्पादन कर सकता है। जितना अधिक सटीक आप इसे बेहतर बना सकते हैं, लेकिन इसे तेजी से रखना अधिक महत्वपूर्ण है।
stakx -

27

आपको पहले से ही इसकी मूल परिभाषा मिल गई है कि वे क्या हैं । संक्षेप में, यदि आप को लागू IEquatable<T>वर्ग पर T, Equalsप्रकार का ऑब्जेक्ट पर विधि Tआपको बताता है कि वस्तु ही (एक समानता के लिए परीक्षण किया जा रहा) एक ही प्रकार का एक और उदाहरण के बराबर है T। जबकि, आम तौर पर के उदाहरणों के दायरे के बाहर, IEqualityComparer<T>किसी भी दो उदाहरणों की समानता का परीक्षण करने Tके लिए है T

जैसा कि वे किस लिए हैं, पहली बार में भ्रमित हो सकते हैं। परिभाषा से यह स्पष्ट होना चाहिए कि इसलिए IEquatable<T>(कक्षा में Tही परिभाषित ) अपनी वस्तुओं / उदाहरणों की विशिष्टता का प्रतिनिधित्व करने के लिए वास्तविक मानक होना चाहिए। HashSet<T>, Dictionary<T, U>(विचार GetHashCodeके रूप में अच्छी तरह से ओवरराइड की गई है), Containsपर List<T>इस बात का आदि बना उपयोग। को लागू करने IEqualityComparer<T>पर Tऊपर उल्लेख सामान्य मामलों में मदद नहीं करता। इसके बाद, इसके IEquatable<T>अलावा किसी अन्य वर्ग पर लागू करने के लिए बहुत कम मूल्य है T। यह:

class MyClass : IEquatable<T>

शायद ही कभी समझ में आता है।

दूसरी ओर

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

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

यह कैसे किया जाना चाहिए।

IEqualityComparer<T>तब उपयोगी हो सकता है जब आपको समानता के एक कस्टम सत्यापन की आवश्यकता होती है, लेकिन सामान्य नियम के रूप में नहीं। उदाहरण के लिए, Personकुछ बिंदु पर एक वर्ग में आपको उनकी उम्र के आधार पर दो लोगों की समानता का परीक्षण करने की आवश्यकता हो सकती है। उस मामले में आप यह कर सकते हैं:

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

उन्हें परखने के लिए, प्रयास करें

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

इसी IEqualityComparer<T>पर Tमतलब नहीं है।

class Person : IEqualityComparer<Person>

यह सच है, काम करता है, लेकिन आँखों को अच्छा नहीं लगता और तर्क को हरा देता है।

आमतौर पर आपको जो चाहिए होता है IEquatable<T>। आदर्श रूप से आपके पास केवल एक ही हो सकता है, IEquatable<T>जबकि कई IEqualityComparer<T>अलग-अलग मानदंडों के आधार पर संभव है।

IEqualityComparer<T>और IEquatable<T>वास्तव में के अनुरूप हैं Comparer<T>और IComparable<T>जो नहीं बल्कि equating से तुलना प्रयोजनों के लिए उपयोग किया जाता है; यहाँ एक अच्छा धागा जहाँ मैंने एक ही उत्तर लिखा है :)


public int GetHashCode(Person obj)लौटना चाहिएobj.GetHashCode()
hyankov

@HristoYankov को वापस लौटना चाहिए obj.Age.GetHashCode। संपादित करेंगे।
नवफाल

कृपया बताएं कि तुलना करने वाले को इस बारे में ऐसी धारणा क्यों बनानी चाहिए कि वह जिस वस्तु की तुलना करता है, उसके बारे में क्या है? आपकी तुलना करने वाले को यह नहीं पता कि व्यक्ति अपने हैश की गणना कैसे करता है, तो आप उसे ओवरराइड क्यों करेंगे?
19

@HristoYankov आपके तुलनाकर्ता को यह नहीं पता है कि व्यक्ति अपने हैश की गणना कैसे करता है - निश्चित रूप से, यही कारण है कि हमें सीधे person.GetHashCodeकहीं भी नहीं बुलाया गया है .... आप इसे क्यों ओवरराइड करेंगे? - हम ओवरराइड करते हैं क्योंकि पूरे बिंदु IEqualityComparerहमारे नियमों के अनुसार एक अलग तुलना कार्यान्वयन है - वे नियम जिन्हें हम वास्तव में अच्छी तरह से जानते हैं।
नवफाल

मेरे उदाहरण में, मुझे अपनी तुलना बंद Ageसंपत्ति के आधार की आवश्यकता है , इसलिए मैं कॉल करता हूं Age.GetHashCode। आयु प्रकार की है int, लेकिन जो कुछ भी है different objects can have equal hash but equal objects cant ever have different hashes। .NET में हैश कोड अनुबंध है । यह एक ऐसा अनुबंध है जिस पर हम आंख मूंदकर विश्वास कर सकते हैं। यकीन है कि हमारे कस्टम तुलनाकर्ताओं को भी इस नियम का पालन करना चाहिए। अगर कभी कॉलिंग someObject.GetHashCodeचीजों को तोड़ता है, तो यह प्रकार के कार्यान्वयनकर्ताओं के साथ समस्या है someObject, यह हमारी समस्या नहीं है।
नवफाल

11

IEqualityComparer उपयोग के लिए है जब दो वस्तुओं की समानता बाहरी रूप से लागू की जाती है, उदाहरण के लिए यदि आप दो प्रकारों के लिए एक तुलनित्र को परिभाषित करना चाहते थे, जिसके लिए आपके पास स्रोत नहीं था, या ऐसे मामलों के लिए जहां दो चीजों के बीच समानता केवल कुछ सीमित संदर्भ में समझ में आती है।

आईईकेबल वस्तु के लिए ही है (जिसे समानता के लिए तुलना की जा रही है) लागू करने के लिए।


आप केवल एक हैं जिन्होंने वास्तव में "प्लग इन इक्वलिटी" (बाहरी उपयोग) मुद्दा उठाया था। प्लस 1.
रॉय नमिर

4

एक दो Tएस की तुलना करता है । दूसरे की तुलना दूसरे से की जा सकती है T। आमतौर पर, आपको केवल समय पर एक का उपयोग करने की आवश्यकता होगी, दोनों में नहीं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.