C # [बंद] में वस्तु गुणों की तुलना


111

यह वही है जो मैं अपने कई अन्य वर्गों द्वारा विरासत में मिली कक्षा पर एक विधि के रूप में आया हूं। विचार यह है कि यह एक ही प्रकार की वस्तुओं के गुणों के बीच सरल तुलना की अनुमति देता है।

अब, यह काम करता है - लेकिन मेरे कोड की गुणवत्ता में सुधार के हित में मुझे लगा कि मैं इसे जांच के लिए फेंक दूंगा। यह कैसे बेहतर / अधिक कुशल / आदि हो सकता है।

/// <summary>
/// Compare property values (as strings)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public bool PropertiesEqual(object comparisonObject)
{

    Type sourceType = this.GetType();
    Type destinationType = comparisonObject.GetType();

    if (sourceType == destinationType)
    {
        PropertyInfo[] sourceProperties = sourceType.GetProperties();
        foreach (PropertyInfo pi in sourceProperties)
        {
            if ((sourceType.GetProperty(pi.Name).GetValue(this, null) == null && destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null) == null))
            {
                // if both are null, don't try to compare  (throws exception)
            }
            else if (!(sourceType.GetProperty(pi.Name).GetValue(this, null).ToString() == destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null).ToString()))
            {
                // only need one property to be different to fail Equals.
                return false;
            }
        }
    }
    else
    {
        throw new ArgumentException("Comparison object must be of the same type.","comparisonObject");
    }

    return true;
}


3
: वैसे तो आप इस एसई साइट के बारे में जानते हैं codereview.stackexchange.com
WIP

कुछ वस्तु तुलना पुस्तकालयों के होते हैं: CompareNETObjects , DeepEqual , AutoCompare , ZCompare ...
nawfal

... और जेनेरिक समानता comparer कार्यान्वयन करने वालों, जिनमें से कुछ कर रहे हैं की एक टन: MemberwiseEqualityComparer , Equ , SemanticComparison , EqualityComparer , DeepEqualityComparer , समानता , Equals.Fody । बाद वाला समूह गुंजाइश और लचीलेपन में सीमित हो सकता है क्योंकि वे क्या हासिल कर सकते हैं।
नवफाल

मैं इस प्रश्न को ऑफ-टॉपिक के रूप में बंद करने के लिए मतदान कर रहा हूं क्योंकि यह कोड समीक्षा की है
Xiaoy312

जवाबों:


160

मैं कोड के एक स्निपेट की तलाश कर रहा था जो यूनिट टेस्ट लिखने में मदद करने के लिए कुछ ऐसा ही करे। यहाँ मैं का उपयोग कर समाप्त हो गया है।

public static bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class 
  {
     if (self != null && to != null)
     {
        Type type = typeof(T);
        List<string> ignoreList = new List<string>(ignore);
        foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
        {
           if (!ignoreList.Contains(pi.Name))
           {
              object selfValue = type.GetProperty(pi.Name).GetValue(self, null);
              object toValue = type.GetProperty(pi.Name).GetValue(to, null);

              if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
              {
                 return false;
              }
           }
        }
        return true;
     }
     return self == to;
  }

संपादित करें:

ऊपर जैसा ही कोड है, लेकिन LINQ और एक्सटेंशन विधियों का उपयोग करता है:

public static bool PublicInstancePropertiesEqual<T>(this T self, T to, params string[] ignore) where T : class
{
    if (self != null && to != null)
    {
        var type = typeof(T);
        var ignoreList = new List<string>(ignore);
        var unequalProperties =
            from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
            where !ignoreList.Contains(pi.Name) && pi.GetUnderlyingType().IsSimpleType() && pi.GetIndexParameters().Length == 0
            let selfValue = type.GetProperty(pi.Name).GetValue(self, null)
            let toValue = type.GetProperty(pi.Name).GetValue(to, null)
            where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
            select selfValue;
        return !unequalProperties.Any();
    }
    return self == to;
}

public static class TypeExtensions
   {
      /// <summary>
      /// Determine whether a type is simple (String, Decimal, DateTime, etc) 
      /// or complex (i.e. custom class with public properties and methods).
      /// </summary>
      /// <see cref="http://stackoverflow.com/questions/2442534/how-to-test-if-type-is-primitive"/>
      public static bool IsSimpleType(
         this Type type)
      {
         return
            type.IsValueType ||
            type.IsPrimitive ||
            new[]
            {
               typeof(String),
               typeof(Decimal),
               typeof(DateTime),
               typeof(DateTimeOffset),
               typeof(TimeSpan),
               typeof(Guid)
            }.Contains(type) ||
            (Convert.GetTypeCode(type) != TypeCode.Object);
      }

      public static Type GetUnderlyingType(this MemberInfo member)
      {
         switch (member.MemberType)
         {
            case MemberTypes.Event:
               return ((EventInfo)member).EventHandlerType;
            case MemberTypes.Field:
               return ((FieldInfo)member).FieldType;
            case MemberTypes.Method:
               return ((MethodInfo)member).ReturnType;
            case MemberTypes.Property:
               return ((PropertyInfo)member).PropertyType;
            default:
               throw new ArgumentException
               (
                  "Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"
               );
         }
      }
   }

बिग टी - काफी पुराना है, लेकिन निश्चित रूप से परीक्षण और सरल तुलना दोनों के लिए एक महान उद्देश्य प्रदान करता है .. धन्यवाद +1
जीम टोलन

1
यह अच्छा है, लेकिन मैंने पाया है कि यह अधिक जटिल वस्तुओं के साथ काम नहीं करता है। उदाहरण के लिए मेरे पास कुछ तारों के साथ एक ऑब्जेक्ट है (यह उनकी तुलना ठीक है) लेकिन फिर इस ऑब्जेक्ट में एक अन्य ऑब्जेक्ट की एक सूची भी है, जिसे यह सही ढंग से तुलना नहीं करता है, इसलिए इसे किसी भी तरह से पुन: व्यवस्थित करने की आवश्यकता है।
रयान थॉमस

1
मुझे पहले मापदंड में दो और मापदंड जोड़ने थे क्योंकि आपको अनुक्रमित संपत्तियों को बाहर करना होगा जो अन्य मामले में अपवाद फेंकते हैं। यहाँ इस त्रुटि के मानदंड हैं: pi.GetIndexParameters ()। लंबाई == 0. और @RyanThomas द्वारा बताई गई समस्या को हल करने के लिए दूसरा मानदंड यह है: pi.GetUnderlyingbype ()। IsSimpleType ()। जैसा कि आप देखेंगे, IsSimpleType वर्ग प्रकार के लिए मौजूद नहीं है और विस्तार है। मैंने इन सभी स्थितियों और विस्तार को जोड़ने के लिए उत्तर को संशोधित किया।
शमूएल

64

अद्यतन: तुलना-नेट-वस्तुओं का नवीनतम संस्करण GitHub पर स्थित है , इसमें NuGet पैकेज और ट्यूटोरियल है । इसे जैसा कहा जा सकता है

//This is the comparison class
CompareLogic compareLogic = new CompareLogic();

ComparisonResult result = compareLogic.Compare(person1, person2);

//These will be different, write out the differences
if (!result.AreEqual)
    Console.WriteLine(result.DifferencesString);

या यदि आपको कुछ कॉन्फ़िगरेशन बदलने की आवश्यकता है, तो उपयोग करें

CompareLogic basicComparison = new CompareLogic() 
{ Config = new ComparisonConfig()
   { MaxDifferences = propertyCount 
     //add other configurations
   }
};

कॉन्फ़िगर करने योग्य मापदंडों की पूरी सूची कंपैरिजनकॉन्फिग.कैंस में है

मूल उत्तर:

आपके कोड में मुझे जो सीमाएँ दिखाई दे रही हैं:

  • सबसे बड़ी बात यह है कि यह गहरी वस्तु तुलना नहीं करता है।

  • यह तत्व की तुलना में एक तत्व द्वारा तत्व नहीं करता है गुण सूची हैं या तत्वों के रूप में सूची हैं (यह एन-स्तर जा सकते हैं)।

  • यह ध्यान में नहीं रखता है कि कुछ प्रकार की संपत्तियों की तुलना नहीं की जानी चाहिए (उदाहरण के लिए, फ़ंडिंग उद्देश्यों के लिए उपयोग की गई एक फ़ंड प्रॉपर्टी, जैसे कि PagedCollectionView वर्ग में)।

  • यह इस बात पर नज़र नहीं रखता है कि वास्तव में कौन से गुण अलग-अलग थे (ताकि आप अपने दावे में दिखा सकें)।

मैं संपत्ति की गहरी तुलना करके संपत्ति का परीक्षण करने के लिए इकाई-परीक्षण के उद्देश्यों के लिए कुछ समाधान ढूंढ रहा था और मैंने इसका उपयोग किया: http://comparenetobjects.codeplex.com

यह सिर्फ एक वर्ग के साथ एक मुफ्त पुस्तकालय है जिसे आप बस इस तरह से उपयोग कर सकते हैं:

var compareObjects = new CompareObjects()
{
    CompareChildren = true, //this turns deep compare one, otherwise it's shallow
    CompareFields = false,
    CompareReadOnly = true,
    ComparePrivateFields = false,
    ComparePrivateProperties = false,
    CompareProperties = true,
    MaxDifferences = 1,
    ElementsToIgnore = new List<string>() { "Filter" }
};

Assert.IsTrue(
    compareObjects.Compare(objectA, objectB), 
    compareObjects.DifferencesString
);

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


2
लिविउ, मैंने सिल्वरलाइट के साथ संगत नहीं होने के बारे में आपकी टिप्पणी पर ध्यान दिया। मैंने इसे सिर्फ सिल्वरलाइट और विंडोज फोन के साथ संगत करने के लिए बदल दिया है। 7. नवीनतम प्राप्त करें। देखें परिवर्तन सेट 74131 परतुलनात्मक स्थिति में SourceControl list changesets
ग्रेग फिनजर

यह आशाजनक लग रहा है। यह कोशिश करो
आजमाया

महान उदाहरण के लिए धन्यवाद! IgnoreObjectTypesविभिन्न प्रकार के होने पर भी, सेटिंग उपयोगी हो सकती है।
सर्गेई ब्रूनोव

संस्करण 2.0 में एक पोर्टेबल क्लास लाइब्रेरी संस्करण है जो सिल्वरलाइट 5+, विंडोज फोन 8+, WinRT 8+, Xamarin IOS और Xamarin Droid
ग्रेग फिनजर

DifferencesStringComparObjects वर्ग में सही किया गया है। लेकिन अब आप इसके बजाय तुलनात्मक रूप से देख सकते हैं:var r = compareObjects.Compare(objectA, objectB); Assert.IsTrue(r.AreEqual, r.DifferencesString);
मैरियानो देसनज़े

6

मुझे लगता है कि ओवरराइड ऑब्जेक्ट # इक्वाल्स () के लिए बेहतर विवरण के लिए पैटर्न का पालन करना सबसे अच्छा होगा
: बिल वैगनर्स इफेक्टिव सी # पढ़ें - आइटम 9 मुझे लगता है

public override Equals(object obOther)
{
  if (null == obOther)
    return false;
  if (object.ReferenceEquals(this, obOther)
    return true;
  if (this.GetType() != obOther.GetType())
    return false;
  # private method to compare members.
  return CompareMembers(this, obOther as ThisClass);
}
  • समानता की जाँच करने वाले तरीकों में भी, आपको या तो सही या गलत पर वापस लौटना चाहिए। या तो वे समान हैं या वे नहीं हैं .. एक अपवाद को फेंकने के बजाय, झूठे लौटें।
  • मैं ओवरराइडिंग ऑब्जेक्ट # इक्वलस पर विचार करूंगा।
  • भले ही आपने इस पर विचार किया होगा, गुणों की तुलना करने के लिए परावर्तन का उपयोग माना जाता है कि यह धीमा है (मेरे पास इसे वापस करने के लिए संख्या नहीं है)। यह मान के लिए डिफ़ॉल्ट व्यवहार है # C में बराबर # और यह अनुशंसा की जाती है कि आप मान प्रकार के लिए समान को ओवरराइड करते हैं और प्रदर्शन के लिए सदस्य की तुलना करते हैं। (पहले मैंने स्पीड-रीड किया क्योंकि आपके पास कस्टम प्रॉपर्टी ऑब्जेक्ट्स का संग्रह है ... मेरा बुरा।)

अपडेट-दिसंबर 2011:

  • बेशक, यदि प्रकार में पहले से ही उत्पादन बराबर है () तो आपको एक और दृष्टिकोण की आवश्यकता है।
  • यदि आप विशेष रूप से परीक्षण उद्देश्यों के लिए अपरिवर्तनीय डेटा संरचनाओं की तुलना करने के लिए इसका उपयोग कर रहे हैं, तो आपको उत्पादन कक्षाओं में एक समान नहीं जोड़ना चाहिए (कोई व्यक्ति समान कार्यान्वयन का पीछा करके परीक्षणों को नली दे सकता है या आप उत्पादन-आवश्यक समान कार्यान्वयन के निर्माण को रोक सकते हैं) ।

मैं ओवरराइडिंग के साथ समस्याओं में भाग गया। ईक्वाल्स () क्योंकि मैं इसे एक बेस क्लास पर लागू करने की कोशिश कर रहा हूं जो विरासत में मिला है ... क्योंकि मुझे पता नहीं है कि जिस क्लास के लिए यह चलाया जा रहा है, उसके लिए मैं नहीं कर सकता। GetHasCode () के लिए एक सभ्य ओवरराइड लागू करें (जब आप समतुल्य () को ओवरराइड करें)।
7

आवश्यकता यह है कि अगर objA.Equals (objB) तो objA.GetHashCode () == objB.GetHashCode ()। GetHashCode को एक श्रेणी के उत्परिवर्तनीय स्थिति / डेटा पर निर्भर नहीं होना चाहिए ... मुझे कक्षा के लिए कुंजियों से आपका मतलब नहीं मिला .. ऐसा कुछ लगता है जिसे हल किया जा सकता है। क्या आधार प्रकार में 'कुंजियाँ' नहीं हैं?
गिशु

6

यदि प्रदर्शन से कोई फर्क नहीं पड़ता है, तो आप उन्हें अनुक्रमित कर सकते हैं और परिणामों की तुलना कर सकते हैं:

var serializer = new XmlSerializer(typeof(TheObjectType));
StringWriter serialized1 = new StringWriter(), serialized2 = new StringWriter();
serializer.Serialize(serialized1, obj1);
serializer.Serialize(serialized2, obj2);
bool areEqual = serialized1.ToString() == serialized2.ToString();

4
यह एक whila पहले की कोशिश की, आपको आश्चर्य होगा कि कितने ऑब्जेक्ट्स सीरियल करने योग्य नहीं हैं ...
10

5

मुझे लगता है कि बिग टी का जवाब काफी अच्छा था, लेकिन गहरी तुलना गायब थी, इसलिए मैंने इसे थोड़ा छोटा किया:

using System.Collections.Generic;
using System.Reflection;

/// <summary>Comparison class.</summary>
public static class Compare
{
    /// <summary>Compare the public instance properties. Uses deep comparison.</summary>
    /// <param name="self">The reference object.</param>
    /// <param name="to">The object to compare.</param>
    /// <param name="ignore">Ignore property with name.</param>
    /// <typeparam name="T">Type of objects.</typeparam>
    /// <returns><see cref="bool">True</see> if both objects are equal, else <see cref="bool">false</see>.</returns>
    public static bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
    {
        if (self != null && to != null)
        {
            var type = self.GetType();
            var ignoreList = new List<string>(ignore);
            foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (ignoreList.Contains(pi.Name))
                {
                    continue;
                }

                var selfValue = type.GetProperty(pi.Name).GetValue(self, null);
                var toValue = type.GetProperty(pi.Name).GetValue(to, null);

                if (pi.PropertyType.IsClass && !pi.PropertyType.Module.ScopeName.Equals("CommonLanguageRuntimeLibrary"))
                {
                    // Check of "CommonLanguageRuntimeLibrary" is needed because string is also a class
                    if (PublicInstancePropertiesEqual(selfValue, toValue, ignore))
                    {
                        continue;
                    }

                    return false;
                }

                if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
                {
                    return false;
                }
            }

            return true;
        }

        return self == to;
    }
}

4

मैं कॉपी और पेस्ट त्रुटियों से बचने के लिए PublicInstancePropertiesEqual विधि में निम्न पंक्ति जोड़ूंगा:

Assert.AreNotSame(self, to);

2

क्या आप अपनी सभी वस्तुओं पर .ToString () को ओवरराइड करते हैं जो गुणों में हैं? अन्यथा, वह दूसरी तुलना शून्य के साथ वापस आ सकती है।

इसके अलावा, उस दूसरी तुलना में, मैं (ए = बी) की तुलना में (ए = बी) के निर्माण के बारे में बाड़ पर हूं, अब से छह महीने / दो साल की पठनीयता के संदर्भ में। लाइन अपने आप में बहुत चौड़ी है, जो ठीक है यदि आपको एक विस्तृत मॉनिटर मिल गया है, लेकिन यह बहुत अच्छी तरह से प्रिंट नहीं हो सकता है। (Nitpick)

क्या आपकी सभी वस्तुएँ हमेशा ऐसे गुणों का उपयोग कर रही हैं जिससे यह कोड काम करेगा? क्या कुछ आंतरिक, गैर-उचित डेटा हो सकते हैं जो एक वस्तु से दूसरी वस्तु में भिन्न हो सकते हैं, लेकिन सभी उजागर डेटा समान हैं? मैं कुछ डेटा के बारे में सोच रहा हूं जो समय के साथ बदल सकते हैं, जैसे दो यादृच्छिक संख्या जनरेटर जो एक ही बिंदु पर एक ही नंबर पर हिट करते हैं, लेकिन जानकारी के दो अलग-अलग अनुक्रमों का उत्पादन करने जा रहे हैं, या बस कोई भी डेटा जो उजागर नहीं होता है संपत्ति इंटरफ़ेस के माध्यम से।


अच्छे अंक -! = ... सहमत, बिंदु लिया गया। ToString () वर्कअराउंड करने का एक प्रयास था ।GetValue किसी ऑब्जेक्ट को लौटाता है (इस तरह तुलना हमेशा झूठी होती है, क्योंकि यह रेफरी की तुलना है) .. क्या कोई बेहतर तरीका है?
नेलटाउनडाउन

यदि GetValue एक ऑब्जेक्ट वापस कर रहा है, तो क्या आप इस फ़ंक्शन के माध्यम से फिर से दोबारा पा सकते हैं? यानी, लौटे ऑब्जेक्ट पर PropertiesEqual को कॉल करें?
एमएमआर

1

यदि आप केवल उसी प्रकार की वस्तुओं की तुलना कर रहे हैं या वंशानुक्रम श्रृंखला को और नीचे कर रहे हैं, तो आपत्ति के बजाय पैरामीटर को अपने आधार प्रकार के रूप में निर्दिष्ट क्यों नहीं करें?

इसके अलावा पैरामीटर पर भी अशक्त जांच करें।

इसके अलावा मैं कोड को और अधिक पठनीय बनाने के लिए 'var' का उपयोग करूँगा (यदि इसका c # 3 कोड है)

इसके अलावा, यदि ऑब्जेक्ट में गुण के रूप में संदर्भ प्रकार हैं, तो आप बस उन पर ToString () कॉल कर रहे हैं जो वास्तव में मूल्यों की तुलना नहीं करते हैं। यदि ToString को अधिलेखित नहीं किया जाता है, तो इसका प्रकार स्ट्रिंग के रूप में नाम वापस करने के लिए जा रहा है जो झूठी-सकारात्मक लौटा सकता है।


संदर्भ प्रकार पर अच्छा बिंदु - मेरे मामले में यह कोई फर्क नहीं पड़ता, लेकिन यह एक अच्छा मौका होगा।
नाखून

1

पहली चीज़ जो मैं सुझाऊंगा वह वास्तविक तुलना को विभाजित करने के लिए होगी ताकि यह थोड़ा अधिक पठनीय हो (मैंने ToString () भी निकाल लिया है - क्या इसकी आवश्यकता है?)।

else {
    object originalProperty = sourceType.GetProperty(pi.Name).GetValue(this, null);
    object comparisonProperty = destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null);

    if (originalProperty != comparisonProperty)
        return false;

अगला सुझाव जितना संभव हो उतना प्रतिबिंब के उपयोग को कम करना होगा - यह वास्तव में धीमा है। मेरा मतलब है, वास्तव में धीमी गति से। यदि आप ऐसा करने जा रहे हैं, तो मैं संपत्ति के संदर्भों को कैशिंग करने का सुझाव दूंगा। मैं परावर्तन एपीआई से पूरी तरह परिचित नहीं हूं, इसलिए यदि यह थोड़ा बंद है, तो इसे संकलित करने के लिए समायोजित करें:

// elsewhere
Dictionary<object, Property[]> lookupDictionary = new Dictionary<object, Property[]>;

Property[] objectProperties = null;
if (lookupDictionary.ContainsKey(sourceType)) {
  objectProperties = lookupProperties[sourceType];
} else {
  // build array of Property references
  PropertyInfo[] sourcePropertyInfos = sourceType.GetProperties();
  Property[] sourceProperties = new Property[sourcePropertyInfos.length];
  for (int i=0; i < sourcePropertyInfos.length; i++) {
    sourceProperties[i] = sourceType.GetProperty(pi.Name);
  }
  // add to cache
  objectProperties = sourceProperties;
  lookupDictionary[object] = sourceProperties;
}

// loop through and compare against the instances

हालांकि, मेरा कहना है कि मैं अन्य पोस्टरों से सहमत हूं। यह आलसी और अकुशल खुशबू आ रही है। आपको :-) के बजाय IComparable को लागू करना चाहिए।


मैं अभी IComparable को देख रहा था लेकिन ऐसा लग रहा था कि यह छँटाई और ऑर्डर करने के लिए था .. क्या यह वास्तव में दो वस्तुओं की समानता की तुलना के लिए उपयोगी है?
7

पूरी तरह से, क्योंकि .Equals (ऑब्जेक्ट o) को इस रूप में परिभाषित किया गया है ।CompareTo (o) == 0. इसलिए, बराबरी का निर्धारण करने के लिए ComparesTo () का उपयोग करता है। यह प्रतिबिंब का उपयोग करने की तुलना में बहुत अधिक कुशल (और मानक अभ्यास) होगा।
tsimon

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

1

यहाँ null = null को बराबर मानने के लिए एक संशोधित किया गया है

 private bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
        {
            if (self != null && to != null)
            {
                Type type = typeof(T);
                List<string> ignoreList = new List<string>(ignore);
                foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
                {
                    if (!ignoreList.Contains(pi.Name))
                    {
                        object selfValue = type.GetProperty(pi.Name).GetValue(self, null);
                        object toValue = type.GetProperty(pi.Name).GetValue(to, null);
                        if (selfValue != null)
                        {
                            if (!selfValue.Equals(toValue))
                                return false;
                        }
                        else if (toValue != null)
                            return false;
                    }
                }
                return true;
            }
            return self == to;
        }

क्या होगा अगर मेरे पास एक गहरी वस्तु ग्राफ था, जो पुराने और नए गुणों की सूची को बदलने के लिए ऊपर उपयोग करने का सबसे अच्छा तरीका है?
रॉड

1

मैंने यह करना समाप्त कर दिया:

    public static string ToStringNullSafe(this object obj)
    {
        return obj != null ? obj.ToString() : String.Empty;
    }
    public static bool Compare<T>(T a, T b)
    {
        int count = a.GetType().GetProperties().Count();
        string aa, bb;
        for (int i = 0; i < count; i++)
        {
            aa = a.GetType().GetProperties()[i].GetValue(a, null).ToStringNullSafe();
            bb = b.GetType().GetProperties()[i].GetValue(b, null).ToStringNullSafe();
            if (aa != bb)
            {
                return false;
            }
        }
        return true;
    }

उपयोग:

    if (Compare<ObjectType>(a, b))

अपडेट करें

यदि आप नाम से कुछ गुणों को अनदेखा करना चाहते हैं:

    public static string ToStringNullSafe(this object obj)
    {
        return obj != null ? obj.ToString() : String.Empty;
    }
    public static bool Compare<T>(T a, T b, params string[] ignore)
    {
        int count = a.GetType().GetProperties().Count();
        string aa, bb;
        for (int i = 0; i < count; i++)
        {
            aa = a.GetType().GetProperties()[i].GetValue(a, null).ToStringNullSafe();
            bb = b.GetType().GetProperties()[i].GetValue(b, null).ToStringNullSafe();
            if (aa != bb && ignore.Where(x => x == a.GetType().GetProperties()[i].Name).Count() == 0)
            {
                return false;
            }
        }
        return true;
    }

उपयोग:

    if (MyFunction.Compare<ObjType>(a, b, "Id","AnotherProp"))

1

आप प्रति बार केवल एक बार GetProperties को कॉल करके अपने कोड को अनुकूलित कर सकते हैं:

public static string ToStringNullSafe(this object obj)
{
    return obj != null ? obj.ToString() : String.Empty;
}
public static bool Compare<T>(T a, T b, params string[] ignore)
{
    var aProps = a.GetType().GetProperties();
    var bProps = b.GetType().GetProperties();
    int count = aProps.Count();
    string aa, bb;
    for (int i = 0; i < count; i++)
    {
        aa = aProps[i].GetValue(a, null).ToStringNullSafe();
        bb = bProps[i].GetValue(b, null).ToStringNullSafe();
        if (aa != bb && ignore.Where(x => x == aProps[i].Name).Count() == 0)
        {
            return false;
        }
    }
    return true;
}

1

पूर्णता के लिए मैं http://www.cyotek.com/blog/comparing-the-properties-of-two-objects-via-reflection पर संदर्भ जोड़ना चाहता हूं । इस पृष्ठ पर अधिकांश अन्य उत्तरों की तुलना में इसमें अधिक पूर्ण तर्क है।

हालाँकि, मैं तुलना-नेट-ऑब्जेक्ट्स लाइब्रेरी को पसंद करता हूं https://github.com/GregFinzer/Compare-Net-Objects ( Liviu Trifoi के उत्तर द्वारा संदर्भित )
लाइब्रेरी में NuGet पैकेज http://www.nuget.org/packages है तुलना करें और कॉन्फ़िगर करने के लिए कई विकल्प।


1

सुनिश्चित करें कि वस्तुएँ नहीं हैं null

होने obj1और obj2:

if(obj1 == null )
{
   return false;
}
return obj1.Equals( obj2 );

क्या होगा अगर वे दोनों शून्य हैं? वे तो बराबर नहीं हैं?
एमएमआर

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

ठीक है, इस मामले के लिए मैं दो वस्तुओं का परीक्षण कर रहा हूं, एक नव निर्मित, एक सत्र से। दोनों की तुलना .Equals () झूठी होने पर भी दोनों की समान संपत्ति मान होने पर भी
7

0

यह तब भी काम करता है, जब ऑब्जेक्ट अलग-अलग हों। आप उपयोगिताओं के वर्ग में विधियों को अनुकूलित कर सकते हैं शायद आप निजी गुणों की तुलना करना चाहते हैं ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class ObjectA
{
    public string PropertyA { get; set; }
    public string PropertyB { get; set; }
    public string PropertyC { get; set; }
    public DateTime PropertyD { get; set; }

    public string FieldA;
    public DateTime FieldB;
}

class ObjectB
{
    public string PropertyA { get; set; }
    public string PropertyB { get; set; }
    public string PropertyC { get; set; }
    public DateTime PropertyD { get; set; }


    public string FieldA;
    public DateTime FieldB;


}

class Program
{
    static void Main(string[] args)
    {
        // create two objects with same properties
        ObjectA a = new ObjectA() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" };
        ObjectB b = new ObjectB() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" };

        // add fields to those objects
        a.FieldA = "hello";
        b.FieldA = "Something differnt";

        if (a.ComparePropertiesTo(b))
        {
            Console.WriteLine("objects have the same properties");
        }
        else
        {
            Console.WriteLine("objects have diferent properties!");
        }


        if (a.CompareFieldsTo(b))
        {
            Console.WriteLine("objects have the same Fields");
        }
        else
        {
            Console.WriteLine("objects have diferent Fields!");
        }

        Console.Read();
    }
}

public static class Utilities
{
    public static bool ComparePropertiesTo(this Object a, Object b)
    {
        System.Reflection.PropertyInfo[] properties = a.GetType().GetProperties(); // get all the properties of object a

        foreach (var property in properties)
        {
            var propertyName = property.Name;

            var aValue = a.GetType().GetProperty(propertyName).GetValue(a, null);
            object bValue;

            try // try to get the same property from object b. maybe that property does
                // not exist! 
            {
                bValue = b.GetType().GetProperty(propertyName).GetValue(b, null);
            }
            catch
            {
                return false;
            }

            if (aValue == null && bValue == null)
                continue;

            if (aValue == null && bValue != null)
                return false;

            if (aValue != null && bValue == null)
               return false;

            // if properties do not match return false
            if (aValue.GetHashCode() != bValue.GetHashCode())
            {
                return false;
            }
        }

        return true;
    }



    public static bool CompareFieldsTo(this Object a, Object b)
    {
        System.Reflection.FieldInfo[] fields = a.GetType().GetFields(); // get all the properties of object a

        foreach (var field in fields)
        {
            var fieldName = field.Name;

            var aValue = a.GetType().GetField(fieldName).GetValue(a);

            object bValue;

            try // try to get the same property from object b. maybe that property does
            // not exist! 
            {
                bValue = b.GetType().GetField(fieldName).GetValue(b);
            }
            catch
            {
                return false;
            }

            if (aValue == null && bValue == null)
               continue;

            if (aValue == null && bValue != null)
               return false;

            if (aValue != null && bValue == null)
               return false;


            // if properties do not match return false
            if (aValue.GetHashCode() != bValue.GetHashCode())
            {
                return false;
            }
        }

        return true;
    }


}

यह कोड 100% कुशल नहीं है। यह उदाहरण के लिए कुछ स्थितियों में काम नहीं करता है यदि इसमें टाइप ऑब्जेक्ट की संपत्ति है।
टोनो नाम

0

ऊपर Liviu के जवाब पर अपडेट करें - ComparObjects.DifferencesString को हटा दिया गया है।

एक इकाई परीक्षण में यह अच्छी तरह से काम करता है:

CompareLogic compareLogic = new CompareLogic();
ComparisonResult result = compareLogic.Compare(object1, object2);
Assert.IsTrue(result.AreEqual);

1
यह बहुत अच्छा है कि आपने डेप्रिसिएशन को ठीक कर दिया है, लेकिन मुझे लगता है कि यह उत्तर वास्तव में लिवियू के उत्तर में एक टिप्पणी होना चाहिए। विशेष रूप से क्योंकि आपके नमूना कोड (Liviu की तुलना में) की तुलना कंप्लोगिक (जो मुझे यकीन है कि महत्वपूर्ण हैं) के मापदंडों का अभाव है , और यह भी जोरदार संदेश (जो कि एक था)। Assert.IsTrue(result.AreEqual, result.DifferencesString);
मुखर के

0

यह विधि propertiesकक्षा की होगी और प्रत्येक के लिए मूल्यों की तुलना करेगी property। यदि कोई मूल्य भिन्न हैं, तो यह होगा return false, अन्यथा यह होगा return true

public 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 (Object1 == null || object2 == null)
        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;
}

उपयोग:

bool isEqual = Compare<Employee>(Object1, Object2)


0

@Nawfal: s उत्तर पर विस्तार करने के लिए, मैं समान इकाई नामों की तुलना करने के लिए अपनी इकाई परीक्षणों में विभिन्न प्रकारों की वस्तुओं का परीक्षण करने के लिए इसका उपयोग करता हूं। मेरे मामले में डेटाबेस इकाई और डीटीओ।

मेरे परीक्षण में इस तरह का इस्तेमाल किया;

Assert.IsTrue(resultDto.PublicInstancePropertiesEqual(expectedEntity));



public static bool PublicInstancePropertiesEqual<T, Z>(this T self, Z to, params string[] ignore) where T : class
{
    if (self != null && to != null)
    {
        var type = typeof(T);
        var type2 = typeof(Z);
        var ignoreList = new List<string>(ignore);
        var unequalProperties =
           from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
           where !ignoreList.Contains(pi.Name)
           let selfValue = type.GetProperty(pi.Name).GetValue(self, null)
           let toValue = type2.GetProperty(pi.Name).GetValue(to, null)
           where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
           select selfValue;
           return !unequalProperties.Any();
    }
    return self == null && to == null;
}

0

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

public abstract class ValueObject<T> where T : ValueObject<T>
{
    protected abstract IEnumerable<object> GetAttributesToIncludeInEqualityCheck();

    public override bool Equals(object other)
    {
        return Equals(other as T);
    }

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

        return GetAttributesToIncludeInEqualityCheck()
            .SequenceEqual(other.GetAttributesToIncludeInEqualityCheck());
    }

    public static bool operator ==(ValueObject<T> left, ValueObject<T> right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(ValueObject<T> left, ValueObject<T> right)
    {
        return !(left == right);
    }

    public override int GetHashCode()
    {
        int hash = 17;
        foreach (var obj in this.GetAttributesToIncludeInEqualityCheck())
            hash = hash * 31 + (obj == null ? 0 : obj.GetHashCode());

        return hash;
    }
}

और वस्तुओं की तुलना करने के लिए बाद में इस अमूर्त वर्ग का उपयोग करें

public class Meters : ValueObject<Meters>
{
    ...

    protected decimal DistanceInMeters { get; private set; }

    ...

    protected override IEnumerable<object> GetAttributesToIncludeInEqualityCheck()
    {
        return new List<Object> { DistanceInMeters };
    }
}

0

अरस अलीनिन से प्रेरित मेरा समाधान ऊपर दिया गया है जहाँ मैंने एक तुलना वस्तु के स्तर और तुलना परिणामों के लिए एक कस्टम वस्तु को जोड़ा है। मैं भी वस्तु नाम के साथ संपत्ति का नाम पाने के लिए इच्छुक हूं:

    public static IEnumerable<ObjectPropertyChanged> GetPublicSimplePropertiesChanged<T>(this T previous, T proposedChange,
     string[] namesOfPropertiesToBeIgnored) where T : class
    {
        return GetPublicGenericPropertiesChanged(previous, proposedChange, namesOfPropertiesToBeIgnored, true, null, null);
    }

    public static IReadOnlyList<ObjectPropertyChanged> GetPublicGenericPropertiesChanged<T>(this T previous, T proposedChange,
        string[] namesOfPropertiesToBeIgnored) where T : class
    {
        return GetPublicGenericPropertiesChanged(previous, proposedChange, namesOfPropertiesToBeIgnored, false, null, null);
    }

    /// <summary>
    /// Gets the names of the public properties which values differs between first and second objects.
    /// Considers 'simple' properties AND for complex properties without index, get the simple properties of the children objects.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="previous">The previous object.</param>
    /// <param name="proposedChange">The second object which should be the new one.</param>
    /// <param name="namesOfPropertiesToBeIgnored">The names of the properties to be ignored.</param>
    /// <param name="simpleTypeOnly">if set to <c>true</c> consider simple types only.</param>
    /// <param name="parentTypeString">The parent type string. Meant only for recursive call with simpleTypeOnly set to <c>true</c>.</param>
    /// <param name="secondType">when calling recursively, the current type of T must be clearly defined here, as T will be more generic (using base class).</param>
    /// <returns>
    /// the names of the properties
    /// </returns>
    private static IReadOnlyList<ObjectPropertyChanged> GetPublicGenericPropertiesChanged<T>(this T previous, T proposedChange,
        string[] namesOfPropertiesToBeIgnored, bool simpleTypeOnly, string parentTypeString, Type secondType) where T : class
    {
        List<ObjectPropertyChanged> propertiesChanged = new List<ObjectPropertyChanged>();

        if (previous != null && proposedChange != null)
        {
            var type = secondType == null ? typeof(T) : secondType;
            string typeStr = parentTypeString + type.Name + ".";
            var ignoreList = namesOfPropertiesToBeIgnored.CreateList();
            IEnumerable<IEnumerable<ObjectPropertyChanged>> genericPropertiesChanged =
                from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                where !ignoreList.Contains(pi.Name) && pi.GetIndexParameters().Length == 0 
                    && (!simpleTypeOnly || simpleTypeOnly && pi.PropertyType.IsSimpleType())
                let firstValue = type.GetProperty(pi.Name).GetValue(previous, null)
                let secondValue = type.GetProperty(pi.Name).GetValue(proposedChange, null)
                where firstValue != secondValue && (firstValue == null || !firstValue.Equals(secondValue))
                let subPropertiesChanged = simpleTypeOnly || pi.PropertyType.IsSimpleType()
                    ? null
                    : GetPublicGenericPropertiesChanged(firstValue, secondValue, namesOfPropertiesToBeIgnored, true, typeStr, pi.PropertyType)
                let objectPropertiesChanged = subPropertiesChanged != null && subPropertiesChanged.Count() > 0
                    ? subPropertiesChanged
                    : (new ObjectPropertyChanged(proposedChange.ToString(), typeStr + pi.Name, firstValue.ToStringOrNull(), secondValue.ToStringOrNull())).CreateList()
                select objectPropertiesChanged;

            if (genericPropertiesChanged != null)
            {   // get items from sub lists
                genericPropertiesChanged.ForEach(a => propertiesChanged.AddRange(a));
            }
        }
        return propertiesChanged;
    }

तुलना परिणामों को संग्रहीत करने के लिए निम्न वर्ग का उपयोग करना

[System.Serializable]
public class ObjectPropertyChanged
{
    public ObjectPropertyChanged(string objectId, string propertyName, string previousValue, string changedValue)
    {
        ObjectId = objectId;
        PropertyName = propertyName;
        PreviousValue = previousValue;
        ProposedChangedValue = changedValue;
    }

    public string ObjectId { get; set; }

    public string PropertyName { get; set; }

    public string PreviousValue { get; set; }

    public string ProposedChangedValue { get; set; }
}

और एक नमूना इकाई परीक्षण:

    [TestMethod()]
    public void GetPublicGenericPropertiesChangedTest1()
    {
        // Define objects to test
        Function func1 = new Function { Id = 1, Description = "func1" };
        Function func2 = new Function { Id = 2, Description = "func2" };
        FunctionAssignment funcAss1 = new FunctionAssignment
        {
            Function = func1,
            Level = 1
        };
        FunctionAssignment funcAss2 = new FunctionAssignment
        {
            Function = func2,
            Level = 2
        };

        // Main test: read properties changed
        var propertiesChanged = Utils.GetPublicGenericPropertiesChanged(funcAss1, funcAss2, null);

        Assert.IsNotNull(propertiesChanged);
        Assert.IsTrue(propertiesChanged.Count == 3);
        Assert.IsTrue(propertiesChanged[0].PropertyName == "FunctionAssignment.Function.Description");
        Assert.IsTrue(propertiesChanged[1].PropertyName == "FunctionAssignment.Function.Id");
        Assert.IsTrue(propertiesChanged[2].PropertyName == "FunctionAssignment.Level");
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.