दो जटिल वस्तुओं की तुलना करने का सबसे अच्छा तरीका


112

मेरे पास दो जटिल वस्तुएं हैं जैसे Object1और Object2उनके पास बाल वस्तुओं के लगभग 5 स्तर हैं।

मुझे यह कहने की सबसे तेज़ विधि चाहिए कि वे समान हैं या नहीं।

यह C # 4.0 में कैसे किया जा सकता है?

जवाबों:


101

लागू करें IEquatable<T>(आमतौर पर अपने सभी कस्टम प्रकारों पर विरासत में मिली Object.Equalsऔर Object.GetHashCodeतरीकों को पछाड़कर )। मिश्रित प्रकारों के मामले में, समाहित प्रकारों के Equalsभीतर निहित प्रकारों की विधि लागू करें । निहित संग्रहों के लिए, SequenceEqualविस्तार विधि का उपयोग करें , जो आंतरिक रूप से IEquatable<T>.Equalsया Object.Equalsप्रत्येक तत्व पर कॉल करता है। इस दृष्टिकोण को स्पष्ट रूप से आपको अपने प्रकार की परिभाषाओं का विस्तार करने की आवश्यकता होगी, लेकिन इसके परिणाम क्रमिकता वाले किसी भी सामान्य समाधान की तुलना में तेज़ हैं।

संपादित करें : यहां नेस्टिंग के तीन स्तरों के साथ एक आकस्मिक उदाहरण है।

मान प्रकारों के लिए, आप आमतौर पर केवल उनके Equalsतरीके को कॉल कर सकते हैं । यहां तक ​​कि अगर फ़ील्ड या गुण कभी भी स्पष्ट रूप से असाइन नहीं किए गए थे, तब भी उनके पास एक डिफ़ॉल्ट मान होगा।

संदर्भ प्रकारों के लिए, आपको पहले कॉल करना चाहिए ReferenceEquals, जो संदर्भ समानता के लिए जांच करता है - यह एक दक्षता को बढ़ावा देने के रूप में काम करेगा जब आप उसी वस्तु को संदर्भित करते हैं। यह उन मामलों को भी हैंडल करेगा जहां दोनों संदर्भ शून्य हैं। यदि वह चेक विफल हो जाता है, तो पुष्टि करें कि आपके इंस्टेंस का क्षेत्र या संपत्ति शून्य नहीं है (बचने के लिए NullReferenceException) और इसकी Equalsविधि को कॉल करें । चूंकि हमारे सदस्य ठीक से टाइप किए गए हैं, इसलिए IEquatable<T>.Equalsओवरराइड Object.Equalsविधि (जिसका निष्पादन टाइप कास्ट के कारण मामूली धीमा होगा) को दरकिनार करते हुए, विधि को सीधे बुलाया जाता है ।

जब आप ओवरराइड करते हैं Object.Equals, तो आपको ओवरराइड करने की भी उम्मीद होती है Object.GetHashCode; मैं संक्षिप्तता की खातिर ऐसा नीचे नहीं करता था।

public class Person : IEquatable<Person>
{
    public int Age { get; set; }
    public string FirstName { get; set; }
    public Address Address { get; set; }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as Person);
    }

    public bool Equals(Person other)
    {
        if (other == null)
            return false;

        return this.Age.Equals(other.Age) &&
            (
                object.ReferenceEquals(this.FirstName, other.FirstName) ||
                this.FirstName != null &&
                this.FirstName.Equals(other.FirstName)
            ) &&
            (
                object.ReferenceEquals(this.Address, other.Address) ||
                this.Address != null &&
                this.Address.Equals(other.Address)
            );
    }
}

public class Address : IEquatable<Address>
{
    public int HouseNo { get; set; }
    public string Street { get; set; }
    public City City { get; set; }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as Address);
    }

    public bool Equals(Address other)
    {
        if (other == null)
            return false;

        return this.HouseNo.Equals(other.HouseNo) &&
            (
                object.ReferenceEquals(this.Street, other.Street) ||
                this.Street != null &&
                this.Street.Equals(other.Street)
            ) &&
            (
                object.ReferenceEquals(this.City, other.City) ||
                this.City != null &&
                this.City.Equals(other.City)
            );
    }
}

public class City : IEquatable<City>
{
    public string Name { get; set; }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as City);
    }

    public bool Equals(City other)
    {
        if (other == null)
            return false;

        return
            object.ReferenceEquals(this.Name, other.Name) ||
            this.Name != null &&
            this.Name.Equals(other.Name);
    }
}

अद्यतन : यह उत्तर कई साल पहले लिखा गया था। तब से, मैंने IEquality<T>ऐसे परिदृश्यों के लिए परिवर्तनशील प्रकारों को लागू करने से दूर रहना शुरू कर दिया है। : वहाँ समानता के दो विचार कर रहे हैं पहचान और तुल्यता । एक मेमोरी प्रतिनिधित्व स्तर पर, ये "संदर्भ समानता" और "मूल्य समानता" ( समानता तुलना देखें ) के रूप में लोकप्रिय रूप से प्रतिष्ठित हैं । हालाँकि, समान अंतर एक डोमेन स्तर पर भी लागू हो सकता है। मान लीजिए कि आपकी Personकक्षा में एक PersonIdसंपत्ति है, जो विशिष्ट वास्तविक दुनिया के प्रति अद्वितीय है। क्या एक ही PersonIdलेकिन भिन्न Ageमान वाली दो वस्तुओं को समान या अलग माना जाना चाहिए? उपरोक्त उत्तर मानता है कि एक तुल्यता के बाद है। हालाँकि, के कई usages हैंIEquality<T>इंटरफ़ेस, जैसे संग्रह, कि मान लेते हैं कि इस तरह के कार्यान्वयन पहचान के लिए प्रदान करते हैं । उदाहरण के लिए, यदि आप आबादी कर रहे हैं, तो आप HashSet<T>आमतौर TryGetValue(T,T)पर मौजूदा तत्वों को वापस करने के लिए एक कॉल की उम्मीद करेंगे जो आपके तर्क की पहचान को साझा करते हैं, जरूरी नहीं कि समान तत्व जिनकी सामग्री पूरी तरह से समान हो। इस धारणा को नोटों द्वारा लागू किया गया है GetHashCode:

सामान्य तौर पर, परिवर्तनशील संदर्भ प्रकारों के लिए, आपको GetHashCode()केवल तभी ओवरराइड करना चाहिए :

  • आप उन फ़ील्ड से हैश कोड की गणना कर सकते हैं जो परस्पर नहीं हैं; या
  • आप यह सुनिश्चित कर सकते हैं कि उत्परिवर्तित वस्तु का हैश कोड परिवर्तित नहीं होता है, जबकि ऑब्जेक्ट एक संग्रह में निहित होता है जो उसके हैश कोड पर निर्भर करता है।

मुझे RIA सेवाओं के माध्यम से यह वस्तु मिलती है ... क्या मैं उन वस्तुओं के लिए IEquatable <Foo> का उपयोग कर सकता हूं और WPF क्लाइंट के तहत प्राप्त कर सकता हूं?
डेवलपर

1
आपका मतलब है कि कक्षाएं स्वतः उत्पन्न होती हैं? मैंने RIA सेवाओं का उपयोग नहीं किया है, लेकिन मैं मानूंगा कि किसी भी उत्पन्न वर्ग को घोषित किया जाएगा partial- जिस स्थिति में, हां, आप Equalsमैन्युअल रूप से जोड़े गए आंशिक वर्ग घोषणा के माध्यम से उनकी विधि को लागू कर सकते हैं, जो स्वतः-जनरेट किए गए फ़ील्ड / गुणों को संदर्भित करता है। एक।
डगलस

क्या होगा यदि "पता पता" वास्तव में "पता [] स्वीकार करता है", तो इसे कैसे लागू किया जाएगा?
गियोमी

2
आप Enumerable.SequenceEqualसरणियों पर LINQ विधि कह सकते हैं this.Addresses.SequenceEqual(other.Addresses):। यह आंतरिक रूप Address.Equalsसे संबंधित पतों के प्रत्येक जोड़े के लिए आपके तरीके को कॉल करेगा , यह देखते हुए कि Addressक्लास IEquatable<Address>इंटरफ़ेस को लागू करता है।
डगलस

2
तुलना की एक और श्रेणी जो एक डेवलपर जाँच सकता है वह है "वर्क्सलाइक"। मेरे लिए, इसका मतलब यह है कि भले ही दो उदाहरणों में कुछ असमान संपत्ति मूल्य हो सकते हैं, कार्यक्रम दो उदाहरणों को संसाधित करने से एक ही परिणाम का उत्पादन करेगा।
जॉन कुर्तज़

95

दोनों वस्तुओं को क्रमबद्ध करें और परिणामी तारों की तुलना करें


1
मैं नहीं देखता कि वहाँ क्यों होगा। सीरियलाइज़ेशन आम तौर पर एक अनुकूलित प्रक्रिया है और आपको किसी भी मामले में प्रत्येक संपत्ति के मूल्य तक पहुंचने की आवश्यकता है।
जोएलफैन

5
बहुत बड़ी लागत है। आप एक डेटा स्ट्रीम उत्पन्न कर रहे हैं, तार जोड़ रहे हैं, और फिर स्ट्रिंग समानता का परीक्षण कर रहे हैं। उस में बस परिमाण के आदेश। क्रमबद्धता का उल्लेख नहीं करना डिफ़ॉल्ट रूप से प्रतिबिंब का उपयोग करने वाला है।
जेरोम हाल्टोम

2
डेटा स्ट्रीम कोई बड़ी बात नहीं है, मैं यह नहीं देखता कि आपको स्ट्रिंग्स को जोड़ने की आवश्यकता क्यों होगी ... स्ट्रिंग समानता का परीक्षण करना वहां से सबसे अधिक अनुकूलित संचालन में से एक है .... आपके पास प्रतिबिंब के साथ एक बिंदु हो सकता है ... लेकिन पर अन्य तरीकों की तुलना में संपूर्ण क्रमांकन "परिमाण के आदेश" नहीं होंगे। यदि आपको प्रदर्शन समस्याओं पर संदेह है तो आपको बेंचमार्क करना चाहिए ... मुझे इस पद्धति के साथ प्रदर्शन की समस्याओं का अनुभव नहीं हुआ है
जोएलफैन

12
मैं +1यह सिर्फ इसलिए कर रहा हूं क्योंकि मैंने इस तरह मूल्यों-आधारित समानता की तुलना करने के बारे में कभी नहीं सोचा है। यह अच्छा और सरल है। इस कोड के साथ कुछ बेंचमार्क देखना साफ-सुथरा होगा।
थॉमस

1
यह एक अच्छा समाधान नहीं है क्योंकि दोनों क्रमबद्धता एक समान तरीके से गलत हो सकती है। उदाहरण के लिए, स्रोत वस्तु के कुछ गुणों को क्रमबद्ध नहीं किया जा सकता है और जब गंतव्य वस्तु में अशक्त करने के लिए सेट किया जाएगा। ऐसे मामले में, स्ट्रिंग्स की तुलना करने वाला आपका परीक्षण पास हो जाएगा, लेकिन दोनों ऑब्जेक्ट वास्तव में समान नहीं हैं!
stackMeUp

35

आप इस समस्या को हल करने के लिए विस्तार विधि, पुनरावृत्ति का उपयोग कर सकते हैं:

public static bool DeepCompare(this object obj, object another)
{     
  if (ReferenceEquals(obj, another)) return true;
  if ((obj == null) || (another == null)) return false;
  //Compare two object's class, return false if they are difference
  if (obj.GetType() != another.GetType()) return false;

  var result = true;
  //Get all properties of obj
  //And compare each other
  foreach (var property in obj.GetType().GetProperties())
  {
      var objValue = property.GetValue(obj);
      var anotherValue = property.GetValue(another);
      if (!objValue.Equals(anotherValue)) result = false;
  }

  return result;
 }

public static bool CompareEx(this object obj, object another)
{
 if (ReferenceEquals(obj, another)) return true;
 if ((obj == null) || (another == null)) return false;
 if (obj.GetType() != another.GetType()) return false;

 //properties: int, double, DateTime, etc, not class
 if (!obj.GetType().IsClass) return obj.Equals(another);

 var result = true;
 foreach (var property in obj.GetType().GetProperties())
 {
    var objValue = property.GetValue(obj);
    var anotherValue = property.GetValue(another);
    //Recursion
    if (!objValue.DeepCompare(anotherValue))   result = false;
 }
 return result;
}

या Json का उपयोग करके तुलना करें (यदि ऑब्जेक्ट बहुत जटिल है) तो आप Newtonsoft.Json का उपयोग कर सकते हैं:

public static bool JsonCompare(this object obj, object another)
{
  if (ReferenceEquals(obj, another)) return true;
  if ((obj == null) || (another == null)) return false;
  if (obj.GetType() != another.GetType()) return false;

  var objJson = JsonConvert.SerializeObject(obj);
  var anotherJson = JsonConvert.SerializeObject(another);

  return objJson == anotherJson;
}

1
पहला उपाय महान है! मुझे पसंद है कि आपको सीरियल को जज करने या ऑब्जेक्ट्स में किसी भी कोड को जोड़ने के लिए खुद को लागू करने की आवश्यकता नहीं है। उपयुक्त जब आप सिर्फ यूनिट परीक्षणों के लिए तुलना कर रहे हैं। क्या मैं सुझाव के मामले में एक सरल तुलना जोड़ने का सुझाव दे सकता हूं और दूसरा समान दोनों समान शून्य हो सकता है? यह अशक्त करने से बचने के लिए एक NullReferenceException को फेंक दिया जाएगा। इससे बचने की कोशिश करें। () // ReSharper एक बार RedundantJumpStatement को निष्क्रिय कर दें अगर (objValue == एक और वीलु) जारी रहे; // अशक्त संदर्भ गार्ड अगर (? objValue.Equals (एक औरValue)) विफल (अपेक्षित, वास्तविक);
मार्क कॉनवे

3
क्या DeepCompareकेवल CompareExपुनरावर्ती कॉल करने के बजाय उपयोग करने का कोई कारण है ?
एपोस्टोलोव

3
यह पूरी संरचना की अनावश्यक रूप से तुलना कर सकता है। के resultसाथ प्रतिस्थापित return falseकरने से यह अधिक कुशल हो जाएगा।
टिम सिलवेस्टर

24

यदि आप IEquatable को लागू नहीं करना चाहते हैं, तो आप हमेशा सभी गुणों की तुलना करने के लिए परावर्तन का उपयोग कर सकते हैं: - यदि वे मूल्य प्रकार हैं, तो बस उनकी तुलना करें-यदि वे संदर्भ प्रकार हैं, तो इसके "आंतरिक" गुणों की तुलना करने के लिए फ़ंक्शन को पुन: कॉल करें। ।

मैं प्रदर्शन के बारे में नहीं सोच रहा हूं, बल्कि सादगी के बारे में सोच रहा हूं। यह निर्भर करता है, हालांकि आपकी वस्तुओं के सटीक डिजाइन पर। यह आपकी वस्तुओं के आकार के आधार पर जटिल हो सकता है (उदाहरण के लिए यदि गुणों के बीच चक्रीय निर्भरताएं हैं)। हालाँकि, वहाँ कई समाधान हैं जो आप उपयोग कर सकते हैं, जैसे कि यह एक:

एक अन्य विकल्प ऑब्जेक्ट को टेक्स्ट के रूप में क्रमबद्ध करना है, उदाहरण के लिए JSON.NET का उपयोग करना, और क्रमांकन परिणाम की तुलना करना। (JSON.NET संपत्तियों के बीच चक्रीय निर्भरता को संभाल सकता है)।

मुझे नहीं पता कि अगर आप सबसे तेज़ हैं तो इसका मतलब है कि इसे लागू करने का सबसे तेज़ तरीका या तेज़ चलने वाला कोड। जरूरत पड़ने पर आपको जानने से पहले अनुकूलन नहीं करना चाहिए। सभी बुराईयो की जड़ समयपूर्व इष्टतमीकरण है


1
मुझे शायद ही लगता है कि IEquatable<T>कार्यान्वयन समयपूर्व अनुकूलन के मामले के रूप में योग्य है। परावर्तन काफी धीमा होगा। Equalsकस्टम मूल्य प्रकारों के लिए डिफ़ॉल्ट कार्यान्वयन प्रतिबिंब का उपयोग करता है; Microsoft खुद को प्रदर्शन के लिए इसे ओवरराइड करने की सलाह देता है: " Equalsविधि के प्रदर्शन को बेहतर बनाने के लिए एक विशेष प्रकार के लिए विधि को ओवरराइड करें और अधिक बारीकी से प्रकार के लिए समानता की अवधारणा का प्रतिनिधित्व करें।"
डगलस

1
यह इस बात पर निर्भर करता है कि वह कितनी बार बराबरी का तरीका चलाने जा रहा है: 1, 10, 100, 100, मिलियन? इससे बहुत फर्क पड़ेगा। अगर वह कुछ भी लागू करने के बिना एक सामान्य समाधान का उपयोग कर सकता है तो वह कुछ कीमती समय को छोड़ देगा। यदि यह बहुत धीमा है, तो आईईक्यूएट को लागू करने का समय आ गया है (और शायद यह भी एक पठनीय या बुद्धिमान GetHashCode बनाने की कोशिश कर रहा है) जहां तक ​​परावर्तन की गति से मुझे सहमत होना चाहिए, यह धीमा है ... या बहुत धीमा, यह निर्भर करता है कि यह कैसे होता है ( अर्थात् संपत्ति में पुन: उपयोग करने वाले प्रकार और इतने पर, या नहीं)।
जटबे

@ वर्थ 7 यह करता है। कृपया, परियोजना की सामग्री देखें। उदाहरण के लिए परीक्षण दस्तावेजीकरण का एक अच्छा तरीका है। लेकिन, इससे बेहतर है कि अगर आप इसकी तलाश करें, तो आपको एक .chm हेल्प फ़ाइल मिल जाएगी। इसलिए इस परियोजना में अधिकांश परियोजनाओं की तुलना में बहुत बेहतर प्रलेखन है।
जटबे

क्षमा करें, आप सही हैं, मैं "विकी" टैब को पूरी तरह से याद कर रहा हूं। मैं हर किसी को रीडमे में चीजें लिखने के लिए उपयोग कर रहा हूं।
वर्थ 7

9

दोनों वस्तुओं को क्रमबद्ध करें और @JoelFan द्वारा परिणामी तारों की तुलना करें

तो ऐसा करने के लिए, एक स्टैटिक क्लास बनाएं जैसे कि और सभी वस्तुओं का विस्तार करने के लिए एक्सटेंशन्स का उपयोग करें (ताकि आप विधि, संग्रह, आदि के किसी भी तरीके को पास कर सकें)

using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;

public static class MySerializer
{
    public static string Serialize(this object obj)
    {
        var serializer = new DataContractJsonSerializer(obj.GetType());
        using (var ms = new MemoryStream())
        {
            serializer.WriteObject(ms, obj);
            return Encoding.Default.GetString(ms.ToArray());
        }
    }
}

एक बार जब आप किसी अन्य फ़ाइल में इस स्थिर वर्ग का संदर्भ देते हैं, तो आप यह कर सकते हैं:

Person p = new Person { Firstname = "Jason", LastName = "Argonauts" };
Person p2 = new Person { Firstname = "Jason", LastName = "Argonaut" };
//assuming you have already created a class person!
string personString = p.Serialize();
string person2String = p2.Serialize();

अब आप बस इनकी तुलना करने के लिए उपयोग कर सकते हैं। मैं यह जाँचने के लिए उपयोग करता हूँ कि क्या वस्तुएँ संग्रह में भी हैं। यह वास्तव में अच्छी तरह से काम करता है।


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

3
क्या होगा अगर एक नए निर्देशक ने आपको C # को चीर कर पायथन से बदलने की बात कही। डेवलपर्स के रूप में, हमें यह जानने की जरूरत है कि यदि प्रश्न कहीं रुकना है तो क्या होगा। समस्या को अगले पर हल करें। अगर आपको कभी समय मिले, तो वापस
आइए

2
वाक्य रचना और उपयोग में पायथन MATLAB की तरह है। एक स्थिर प्रकार की सुरक्षित भाषा से अजगर की तरह एक मिश्मश लिपि में जाने का वास्तव में अच्छा कारण होना चाहिए।
जॉन एलेक्सियौ

5

मुझे लगता है आप शाब्दिक रूप से एक ही वस्तुओं का जिक्र नहीं कर रहे हैं

Object1 == Object2

आप दोनों के बीच मेमोरी तुलना करने के बारे में सोच रहे होंगे

memcmp(Object1, Object2, sizeof(Object.GetType())

लेकिन यह सी # :) में भी वास्तविक कोड नहीं है। क्योंकि आपका सारा डेटा संभवतः ढेर पर बनाया गया है, स्मृति सन्निहित नहीं है और आप केवल अज्ञेय तरीके से दो वस्तुओं की समानता की तुलना नहीं कर सकते हैं। आप प्रत्येक मान की, एक समय में, एक कस्टम तरीके से तुलना करने जा रहे हैं।

IEquatable<T>अपनी कक्षा में इंटरफ़ेस जोड़ने पर विचार करें , और Equalsअपने प्रकार के लिए एक कस्टम विधि निर्धारित करें। फिर, उस विधि में, मैन्युअल प्रत्येक मान का परीक्षण करें। IEquatable<T>संलग्न प्रकारों पर फिर से जोड़ें यदि आप प्रक्रिया को दोहरा सकते हैं।

class Foo : IEquatable<Foo>
{
  public bool Equals(Foo other)
  {
    /* check all the values */
    return false;
  }
}


3

मुझे वस्तुओं की तुलना करने के लिए यह नीचे फ़ंक्शन मिला।

 static bool Compare<T>(T Object1, T object2)
 {
      //Get the type of the object
      Type type = typeof(T);

      //return false if any of the object is false
      if (object.Equals(Object1, default(T)) || object.Equals(object2, default(T)))
         return false;

     //Loop through each properties inside class and get values for the property from both the objects and compare
     foreach (System.Reflection.PropertyInfo property in type.GetProperties())
     {
          if (property.Name != "ExtensionData")
          {
              string Object1Value = string.Empty;
              string Object2Value = string.Empty;
              if (type.GetProperty(property.Name).GetValue(Object1, null) != null)
                    Object1Value = type.GetProperty(property.Name).GetValue(Object1, null).ToString();
              if (type.GetProperty(property.Name).GetValue(object2, null) != null)
                    Object2Value = type.GetProperty(property.Name).GetValue(object2, null).ToString();
              if (Object1Value.Trim() != Object2Value.Trim())
              {
                  return false;
              }
          }
     }
     return true;
 }

मैं इसका उपयोग कर रहा हूं और यह मेरे लिए ठीक काम कर रहा है।


1
पहले का ifमतलब है कि Compare(null, null) == falseमैं क्या उम्मीद नहीं है।
टिम सिलवेस्टर

3

पहले से ही यहां दिए गए कुछ उत्तरों के आधार पर मैंने ज्यादातर जोएलफैन के जवाब को वापस लेने का फैसला किया । मुझे विस्तार के तरीकों से प्यार है और ये मेरे लिए बहुत अच्छा काम कर रहे हैं जब कोई भी अन्य समाधान मेरी जटिल कक्षाओं की तुलना करने के लिए उनका उपयोग नहीं करेगा।

एक्सटेंशन के तरीके

using System.IO;
using System.Xml.Serialization;

static class ObjectHelpers
{
    public static string SerializeObject<T>(this T toSerialize)
    {
        XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());

        using (StringWriter textWriter = new StringWriter())
        {
            xmlSerializer.Serialize(textWriter, toSerialize);
            return textWriter.ToString();
        }
    }

    public static bool EqualTo(this object obj, object toCompare)
    {
        if (obj.SerializeObject() == toCompare.SerializeObject())
            return true;
        else
            return false;
    }

    public static bool IsBlank<T>(this T obj) where T: new()
    {
        T blank = new T();
        T newObj = ((T)obj);

        if (newObj.SerializeObject() == blank.SerializeObject())
            return true;
        else
            return false;
    }

}

उपयोग के उदाहरण

if (record.IsBlank())
    throw new Exception("Record found is blank.");

if (record.EqualTo(new record()))
    throw new Exception("Record found is blank.");

2

मैं कहूँगा कि:

Object1.Equals(Object2)

वह होगा जो आप खोज रहे हैं। यदि आप देख रहे हैं कि क्या वस्तुएं समान हैं, तो वही है जो आप पूछ रहे हैं।

यदि आप यह देखना चाहते हैं कि क्या सभी चाइल्ड ऑब्जेक्ट समान हैं, तो उन्हें Equals()विधि के साथ लूप के माध्यम से चलाएं ।


2
यदि और केवल यदि वे एक गैर-संदर्भ समानता बराबरी का अधिभार प्रदान करते हैं।
user7116

प्रत्येक वर्ग को अपनी तुलना के अपने तरीके को लागू करना चाहिए। यदि लेखक की कक्षाओं में समतुल्य () पद्धति के लिए कोई ओवरराइड नहीं है, तो वे System.Object () वर्ग की मूल विधि का उपयोग करेंगे, जिससे तर्क में त्रुटियां होंगी।
दीमा

2
public class GetObjectsComparison
{
    public object FirstObject, SecondObject;
    public BindingFlags BindingFlagsConditions= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
}
public struct SetObjectsComparison
{
    public FieldInfo SecondObjectFieldInfo;
    public dynamic FirstObjectFieldInfoValue, SecondObjectFieldInfoValue;
    public bool ErrorFound;
    public GetObjectsComparison GetObjectsComparison;
}
private static bool ObjectsComparison(GetObjectsComparison GetObjectsComparison)
{
    GetObjectsComparison FunctionGet = GetObjectsComparison;
    SetObjectsComparison FunctionSet = new SetObjectsComparison();
    if (FunctionSet.ErrorFound==false)
        foreach (FieldInfo FirstObjectFieldInfo in FunctionGet.FirstObject.GetType().GetFields(FunctionGet.BindingFlagsConditions))
        {
            FunctionSet.SecondObjectFieldInfo =
            FunctionGet.SecondObject.GetType().GetField(FirstObjectFieldInfo.Name, FunctionGet.BindingFlagsConditions);

            FunctionSet.FirstObjectFieldInfoValue = FirstObjectFieldInfo.GetValue(FunctionGet.FirstObject);
            FunctionSet.SecondObjectFieldInfoValue = FunctionSet.SecondObjectFieldInfo.GetValue(FunctionGet.SecondObject);
            if (FirstObjectFieldInfo.FieldType.IsNested)
            {
                FunctionSet.GetObjectsComparison =
                new GetObjectsComparison()
                {
                    FirstObject = FunctionSet.FirstObjectFieldInfoValue
                    ,
                    SecondObject = FunctionSet.SecondObjectFieldInfoValue
                };

                if (!ObjectsComparison(FunctionSet.GetObjectsComparison))
                {
                    FunctionSet.ErrorFound = true;
                    break;
                }
            }
            else if (FunctionSet.FirstObjectFieldInfoValue != FunctionSet.SecondObjectFieldInfoValue)
            {
                FunctionSet.ErrorFound = true;
                break;
            }
        }
    return !FunctionSet.ErrorFound;
}

Recursion के सिद्धांतों का उपयोग करना।
मटन जस्टमे जुने

1

ऐसा करने का एक तरीका यह होगा कि Equals()इसमें शामिल प्रत्येक प्रकार पर ओवरराइड किया जाए। उदाहरण के लिए, आपकी शीर्ष स्तर की वस्तु सभी 5 चाइल्ड ऑब्जेक्ट Equals()की Equals()विधि को कॉल करने के लिए ओवरराइड होगी । उन वस्तुओं को पूरी Equals()तरह से ओवरराइड करना चाहिए , यह मानते हुए कि वे कस्टम ऑब्जेक्ट हैं, और इसी तरह जब तक पूरे पदानुक्रम की तुलना सिर्फ शीर्ष स्तर की वस्तुओं पर समानता की जांच करने से नहीं की जा सकती।



1

जोनाथन के उदाहरण के लिए धन्यवाद। मैंने इसे सभी मामलों (सरणियों, सूचियों, शब्दकोशों, आदिम प्रकारों) के लिए विस्तारित किया।

यह क्रमबद्धता के बिना एक तुलना है और तुलनात्मक वस्तुओं के लिए किसी भी इंटरफेस के कार्यान्वयन की आवश्यकता नहीं है।

        /// <summary>Returns description of difference or empty value if equal</summary>
        public static string Compare(object obj1, object obj2, string path = "")
        {
            string path1 = string.IsNullOrEmpty(path) ? "" : path + ": ";
            if (obj1 == null && obj2 != null)
                return path1 + "null != not null";
            else if (obj2 == null && obj1 != null)
                return path1 + "not null != null";
            else if (obj1 == null && obj2 == null)
                return null;

            if (!obj1.GetType().Equals(obj2.GetType()))
                return "different types: " + obj1.GetType() + " and " + obj2.GetType();

            Type type = obj1.GetType();
            if (path == "")
                path = type.Name;

            if (type.IsPrimitive || typeof(string).Equals(type))
            {
                if (!obj1.Equals(obj2))
                    return path1 + "'" + obj1 + "' != '" + obj2 + "'";
                return null;
            }
            if (type.IsArray)
            {
                Array first = obj1 as Array;
                Array second = obj2 as Array;
                if (first.Length != second.Length)
                    return path1 + "array size differs (" + first.Length + " vs " + second.Length + ")";

                var en = first.GetEnumerator();
                int i = 0;
                while (en.MoveNext())
                {
                    string res = Compare(en.Current, second.GetValue(i), path);
                    if (res != null)
                        return res + " (Index " + i + ")";
                    i++;
                }
            }
            else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type))
            {
                System.Collections.IEnumerable first = obj1 as System.Collections.IEnumerable;
                System.Collections.IEnumerable second = obj2 as System.Collections.IEnumerable;

                var en = first.GetEnumerator();
                var en2 = second.GetEnumerator();
                int i = 0;
                while (en.MoveNext())
                {
                    if (!en2.MoveNext())
                        return path + ": enumerable size differs";

                    string res = Compare(en.Current, en2.Current, path);
                    if (res != null)
                        return res + " (Index " + i + ")";
                    i++;
                }
            }
            else
            {
                foreach (PropertyInfo pi in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
                {
                    try
                    {
                        var val = pi.GetValue(obj1);
                        var tval = pi.GetValue(obj2);
                        if (path.EndsWith("." + pi.Name))
                            return null;
                        var pathNew = (path.Length == 0 ? "" : path + ".") + pi.Name;
                        string res = Compare(val, tval, pathNew);
                        if (res != null)
                            return res;
                    }
                    catch (TargetParameterCountException)
                    {
                        //index property
                    }
                }
                foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
                {
                    var val = fi.GetValue(obj1);
                    var tval = fi.GetValue(obj2);
                    if (path.EndsWith("." + fi.Name))
                        return null;
                    var pathNew = (path.Length == 0 ? "" : path + ".") + fi.Name;
                    string res = Compare(val, tval, pathNew);
                    if (res != null)
                        return res;
                }
            }
            return null;
        }

कोड की आसान नकल के लिए रिपोजिटरी बनाया


1

अब आप json.net का उपयोग कर सकते हैं। बस Nuget पर जाएं और इसे इंस्टॉल करें।

और आप ऐसा कुछ कर सकते हैं:

    public bool Equals(SamplesItem sampleToCompare)
    {
        string myself = JsonConvert.SerializeObject(this);
        string other = JsonConvert.SerializeObject(sampleToCompare);

        return myself == other;
    }

यदि आप कट्टरता प्राप्त करना चाहते हैं तो आप शायद ऑब्जेक्ट के लिए एक विस्तार विधि बना सकते हैं। कृपया ध्यान दें कि यह केवल सार्वजनिक संपत्तियों की तुलना करता है। और यदि आप एक सार्वजनिक संपत्ति को अनदेखा करना चाहते थे जब आप तुलना करते हैं तो आप [JsonIgnore] विशेषता का उपयोग कर सकते हैं।


यदि आपके पास अपनी वस्तुओं में सूचियां हैं और उन सूचियों में हैं तो दोनों वस्तुओं को पार करने की कोशिश एक दुःस्वप्न बनने जा रही है। यदि आप दोनों को क्रमबद्ध करते हैं और फिर तुलना करते हैं तो आपको उस परिदृश्य से निपटना नहीं पड़ेगा।
ashlar64

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