2009-09-02 12 views
88
class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Book> books = new List<Book> 
     { 
      new Book 
      { 
       Name="C# in Depth", 
       Authors = new List<Author> 
       { 
        new Author 
        { 
         FirstName = "Jon", LastName="Skeet" 
        }, 
        new Author 
        { 
         FirstName = "Jon", LastName="Skeet" 
        },      
       } 
      }, 
      new Book 
      { 
       Name="LINQ in Action", 
       Authors = new List<Author> 
       { 
        new Author 
        { 
         FirstName = "Fabrice", LastName="Marguerie" 
        }, 
        new Author 
        { 
         FirstName = "Steve", LastName="Eichert" 
        }, 
        new Author 
        { 
         FirstName = "Jim", LastName="Wooley" 
        }, 
       } 
      }, 
     }; 


     var temp = books.SelectMany(book => book.Authors).Distinct(); 
     foreach (var author in temp) 
     { 
      Console.WriteLine(author.FirstName + " " + author.LastName); 
     } 

     Console.Read(); 
    } 

} 
public class Book 
{ 
    public string Name { get; set; } 
    public List<Author> Authors { get; set; } 
} 
public class Author 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public override bool Equals(object obj) 
    { 
     return true; 
     //if (obj.GetType() != typeof(Author)) return false; 
     //else return ((Author)obj).FirstName == this.FirstName && ((Author)obj).FirstName == this.LastName; 
    } 

} 

यह "LINQ इन एक्शन" में एक उदाहरण पर आधारित है। लिस्टिंग 4.16।अलग-अलग LINQ से ऑब्जेक्ट्स

यह जॉन स्कीट को दो बार प्रिंट करता है। क्यूं कर? मैंने लेखक वर्ग में बराबर विधि को ओवरराइड करने का भी प्रयास किया है। अभी भी अलग काम नहीं लग रहा है। मैं क्या खो रहा हूँ?

संपादित करें: मैंने == और! = ऑपरेटर अधिभार भी जोड़ा है। अभी भी कोई मदद नहीं।

public static bool operator ==(Author a, Author b) 
    { 
     return true; 
    } 
    public static bool operator !=(Author a, Author b) 
    { 
     return false; 
    } 

उत्तर

114

LINQ अलग है कि स्मार्ट जब यह कस्टम वस्तुओं की बात आती नहीं है।

यह सब आपकी सूची को देखता है और देखता है कि इसमें दो अलग-अलग वस्तुएं हैं (यह परवाह नहीं है कि उनके पास सदस्य फ़ील्ड के लिए समान मान हैं)।

एक वर्कअराउंड IEquatable इंटरफ़ेस को लागू करना है जैसा कि here दिखाया गया है।

यदि आप अपनी लेखक कक्षा को संशोधित करते हैं तो इसे काम करना चाहिए।

public class Author : IEquatable<Author> 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 

    public bool Equals(Author other) 
    { 
     if (FirstName == other.FirstName && LastName == other.LastName) 
      return true; 

     return false; 
    } 

    public override int GetHashCode() 
    { 
     int hashFirstName = FirstName == null ? 0 : FirstName.GetHashCode(); 
     int hashLastName = LastName == null ? 0 : LastName.GetHashCode(); 

     return hashFirstName^hashLastName; 
    } 
} 

Try it as DotNetFiddle

+11

आईक्वाटेबल ठीक है लेकिन अपूर्ण है; आपको हमेशा ऑब्जेक्ट। एक्वाल्स() और ऑब्जेक्ट। गेटहाशकोड() को लागू करना चाहिए; IEquatable Equals ऑब्जेक्ट.क्वाल्स को ओवरराइड नहीं करता है, इसलिए गैर-दृढ़ता से टाइप की गई तुलना करने पर यह असफल हो जाएगा, जो अक्सर ढांचे में और हमेशा गैर-जेनेरिक संग्रह में होता है। – AndyM

+0

क्या रेक्स एम ने सुझाव दिया है कि डिक्विंट के ओवरराइड का उपयोग करना बेहतर है जो IEqualityComparer लेता है? मेरा मतलब है कि अगर मैं जाल में गिरना नहीं चाहता तो मुझे क्या करना चाहिए। – Tanmoy

+3

@Tanmoy यह निर्भर करता है। यदि आप चाहते हैं कि लेखक सामान्य रूप से सामान्य ऑब्जेक्ट की तरह व्यवहार करें (यानी केवल संदर्भ समानता) लेकिन अलग-अलग उद्देश्य के लिए नाम मानों की जांच करें, IqualityComparer का उपयोग करें। यदि आप * हमेशा * ऑब्जेक्ट ऑब्जेक्ट्स को नाम मानों के आधार पर तुलना करना चाहते हैं, तो GetHashCode और Equals को ओवरराइड करें, या IEquatable को लागू करें। –

54

Distinct() विधि चेकों संदर्भ प्रकार के लिए समानता के संदर्भ हैं। इसका मतलब है कि यह सचमुच वही ऑब्जेक्ट डुप्लिकेट की तलाश में है, अलग-अलग ऑब्जेक्ट्स जिनमें समान मान नहीं हैं।

एक overload है जो IEqualityComparer लेता है, इसलिए आप यह निर्धारित करने के लिए अलग तर्क निर्दिष्ट कर सकते हैं कि कोई ऑब्जेक्ट किसी अन्य के बराबर है या नहीं।

आप (यानी केवल समानता का संदर्भ) आम तौर पर एक सामान्य वस्तु की तरह व्यवहार करने के लिए लेखक चाहते हैं, लेकिन नाम मूल्यों से अलग जांच समानता के प्रयोजनों के लिए, एक IEqualityComparer का उपयोग करें। यदि आप हमेशा नाम वस्तुओं के आधार पर लेखक ऑब्जेक्ट्स की तुलना करना चाहते हैं, तो GetHashCode ओवरराइड करें और, या इक्वेटेबल लागू करें।

IEqualityComparer इंटरफेस पर दो सदस्य Equals और GetHashCode हैं। यह निर्धारित करने के लिए आपका तर्क है कि दो Author ऑब्जेक्ट बराबर हैं यदि प्रथम और अंतिम नाम तार समान हैं।

public class AuthorEquals : IEqualityComparer<Author> 
{ 
    public bool Equals(Author left, Author right) 
    { 
     if((object)left == null && (object)right == null) 
     { 
      return true; 
     } 
     if((object)left == null || (object)right == null) 
     { 
      return false; 
     } 
     return left.FirstName == right.FirstName && left.LastName == right.LastName; 
    } 

    public int GetHashCode(Author author) 
    { 
     return (author.FirstName + author.LastName).GetHashCode(); 
    } 
} 
+0

धन्यवाद ! आपके GetHashCode() कार्यान्वयन ने मुझे दिखाया कि मैं अभी भी क्या खो रहा था। मैं {pass-in ऑब्जेक्ट} लौट रहा था। .GetHashCode(), {संपत्ति की तुलना के लिए उपयोग नहीं किया जा रहा है} .GetHashCode()। इससे अंतर आया और बताता है कि मेरा अभी भी असफल क्यों रहा - दो अलग-अलग संदर्भों में दो अलग हैंश कोड होंगे। – pelazem

9

आप बराबर() ओवरराइड है, लेकिन यकीन है कि आप भी GetHashCode को ओवरराइड करने के()

+0

+1। आधार HashCode कार्यान्वयन को '^आधार में जोड़ें न जोड़ें।GetHashCode() ' – Dani

20

Distinct() गणनीय में वस्तुओं पर डिफ़ॉल्ट समानता तुलना करता है। यदि आपने Equals() और GetHashCode() पर ओवरराइड नहीं किया है, तो यह object पर डिफ़ॉल्ट कार्यान्वयन का उपयोग करता है, जो संदर्भों की तुलना करता है।

सरल समाधान सभी वर्गों जो वस्तु ग्राफ आप तुलना कर रहे हैं (यानी बुक और लेखक) में भाग लेने के Equals() और GetHashCode() के सही कार्यान्वयन जोड़ना है।

IEqualityComparer इंटरफ़ेस एक सुविधा है कि आप Equals() और एक अलग वर्ग में GetHashCode() लागू करने के लिए आप कक्षाएं आप तुलना करने की आवश्यकता के आंतरिक भागों के लिए पहुँच नहीं है जब की अनुमति देता है, या आप तुलना का एक अलग पद्धति उपयोग कर रहे हैं ।

+0

भाग लेने वाली वस्तुओं के बारे में इस शानदार कॉमेंट के लिए आपका बहुत बहुत धन्यवाद। – suhyura

33

IEquatable, Equals और GetHashCode को लागू करने के बिना एक अन्य समाधान LINQs GroupBy विधि का उपयोग करने और IGrouping से पहले आइटम का चयन करने के लिए है।

var temp = books.SelectMany(book => book.Authors) 
       .GroupBy (y => y.FirstName + y.LastName) 
       .Select (y => y.First()); 

foreach (var author in temp){ 
    Console.WriteLine(author.FirstName + " " + author.LastName); 
} 
+1

इससे मुझे मदद मिली, सिर्फ प्रदर्शन पर विचार करते हुए, यह उपरोक्त तरीकों पर विचार करते हुए, समान गति से प्रदर्शन करता है? – Biswajeet

+0

कार्यान्वयन विधियों के साथ जटिलता से बहुत अच्छा है, और यदि ईएफ का उपयोग करना एसक्यूएल सर्वर पर काम को प्रतिनिधि करेगा। – Zapnologica

+0

जबकि यह विधि काम कर सकती है, – Bellash

5

उपरोक्त उत्तरों गलत हैं !!! एमएसडीएन पर बताए गए विशिष्ट डिफॉल्ट इक्वेटर को लौटाते हैं, जैसा कि कहा गया है डिफ़ॉल्ट संपत्ति जांचती है कि टाइप टी सिस्टम को लागू करता है या नहीं। इक्वेटेबल इंटरफ़ेस और यदि ऐसा है, तो उस कार्यान्वयन का उपयोग करने वाले समानता कॉम्पैयर को लौटाता है। अन्यथा, यह एक EqualityComparer Object.Equals और Object.GetHashCode की ओवरराइड टी

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

कारण कोड आप काम नहीं कर रहे हैं क्योंकि आप firstname == lastname की जांच करते हैं।

https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx और https://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx

7

एक और तरीका उपयोगकर्ता परिभाषित डेटा प्रकार की सूची से अलग मान प्राप्त करने के लिए नहीं है देखें:

YourList.GroupBy(i => i.Id).Select(i => i.First()).ToList(); 

निश्चित रूप से, यह डेटा के विशिष्ट सेट दे देंगे

संबंधित मुद्दे