সি # তে অবজেক্ট বৈশিষ্ট্যের তুলনা [বন্ধ]


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

সম্পাদনা করুন:

উপরের মত একই কোড তবে লিনকিউ এবং এক্সটেনশন পদ্ধতিগুলি ব্যবহার করে:

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
আমাকে প্রথমে মানদণ্ডে যুক্ত করতে হয়েছিল যেখানে আরও দুটি মাপদণ্ড আপনাকে যুক্ত করতে হবে কারণ আপনাকে অন্যান্য সূচকযুক্ত বৈশিষ্ট্য বাদ দিতে হবে যা অন্যান্য ক্ষেত্রে ব্যতিক্রম ছড়িয়ে দেয়। এই ত্রুটির জন্য মাপদণ্ড এখানে: পাই.গেটইন্ডেক্সপ্যারামিটারগুলি ()। দৈর্ঘ্য == ০. এবং @ রায়ান থমাস দ্বারা প্রদত্ত সমস্যাটি সমাধান করার দ্বিতীয় মাপদণ্ডটি হ'ল: পাই.গেটউন্ডারিলিংটাইপ () .সিম্পলটাইপ ()। আপনি দেখতে পাবেন যে, ইস্পল টাইপটি হ'ল এবং এক্সটেনশন যা শ্রেণীর ধরণের জন্য বিদ্যমান নয়। এই সমস্ত শর্ত এবং এক্সটেনশান যুক্ত করতে আমি উত্তরটি পরিবর্তন করেছি।
স্যামুয়েল

64

আপডেট: তুলনা-নেট-অবজেক্টের সর্বশেষতম সংস্করণটি গিটহাবে অবস্থিত , এতে নুগেট প্যাকেজ এবং টিউটোরিয়াল রয়েছে । এটি বলা যেতে পারে

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

কনফিগারযোগ্য পরামিতিগুলির সম্পূর্ণ তালিকা তুলনা - কনফিগ.সি.এস.

আসল উত্তর:

আমি আপনার কোডে সীমাবদ্ধতাগুলি দেখতে পাচ্ছি:

  • সবচেয়ে বড়টি হ'ল এটি কোনও গভীর বস্তুর তুলনা করে না।

  • বৈশিষ্ট্য তালিকা হিসাবে থাকে বা উপাদান হিসাবে তালিকাগুলি থাকে (এটি এন-স্তরগুলিতে যেতে পারে) ক্ষেত্রে এটি উপাদান তুলনা করে কোনও উপাদান করে না।

  • কিছু ধরণের বৈশিষ্ট্যের তুলনা করা উচিত নয় তা বিবেচনায় নেওয়া হয় না (যেমন পেজক্লেকশনভিউ শ্রেণীর মতো ফিল্টারিংয়ের উদ্দেশ্যে ব্যবহৃত একটি ফানক সম্পত্তি)।

  • এটি কী বৈশিষ্ট্যগুলি আসলে আলাদা ছিল তা ট্র্যাক করে না (যাতে আপনি নিজের দৃ as়তার সাথে প্রদর্শন করতে পারেন)।

সম্পত্তির গভীর তুলনা করে সম্পত্তি করার ইউনিট-টেস্টিংয়ের উদ্দেশ্যে আমি আজ কিছু সমাধান খুঁজছিলাম এবং আমি এখানে ব্যবহার করে শেষ করেছি: 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
লিভিউ, আমি ক্লাসটি সিলভারলাইটের সাথে সামঞ্জস্যপূর্ণ না হওয়া সম্পর্কে আপনার মন্তব্য লক্ষ্য করেছি। আমি এটিকে সিলভারলাইট এবং উইন্ডোজ ফোনের সাথে সামঞ্জস্যপূর্ণ হিসাবে পরিবর্তিত করেছি a তুলনা নেটবজেক্টস কোডপ্লেক্স
গ্রেগ ফিনজার

এটি আশাব্যঞ্জক মনে হচ্ছে। এটি ব্যবহার করে দেখতে
যাবেন

মহান উদাহরণ জন্য আপনাকে ধন্যবাদ! এছাড়াও, IgnoreObjectTypesবিভিন্ন ধরণের থাকলে সেটিংটি কার্যকর হতে পারে।
সের্গেই ব্রুনোভ

সংস্করণ ২.০ এর একটি পোর্টেবল ক্লাস লাইব্রেরি সংস্করণ রয়েছে যা সিলভারলাইট 5+, উইন্ডোজ ফোন 8+, উইনআরটি 8+, জামারিন আইওএস এবং জামারিন ড্রয়েডের সাথে সামঞ্জস্যপূর্ণ
গ্রেগ ফিনজার

DifferencesStringতুলনাশাস্ত্র ক্লাসে স্থানচ্যুত করা হয়েছে। তবে এখন আপনি তার পরিবর্তে তুলনা ফলাফল থেকে এটি পেতে পারেন: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);
}
  • এছাড়াও যে পদ্ধতিগুলির মধ্যে সাম্যতা পরীক্ষা করা হয়, সেগুলি সত্য বা মিথ্যা ফিরে পাওয়া উচিত। হয় তারা সমান বা তারা হয় না .. পরিবর্তে একটি ব্যতিক্রম নিক্ষেপ, মিথ্যা ফিরে।
  • আমি # সমান অবজেক্টকে ওভাররাইড বিবেচনা করব।
  • যদিও আপনি এটি অবশ্যই বিবেচনা করেছেন, গুণাবলীর তুলনা করতে প্রতিবিম্বটি ব্যবহার করা ধীরে ধীরে (এটিকে ব্যাক করার জন্য আমার কাছে সংখ্যা নেই)। এটি সি # তে মান টাইপ # সমান এর জন্য ডিফল্ট আচরণ এবং এটি আপনাকে প্রস্তাব দেওয়া হয় যে আপনি মানের ধরণের জন্য সমানকে ওভাররাইড করুন এবং পারফরম্যান্সের জন্য কোনও সদস্যের সাথে তুলনা করুন। (এর আগে আমি কাস্টম সম্পত্তি সংস্থাগুলির সংকলন পেয়েছি বলে আমি এটি দ্রুত-পড়েছি ... আমার খারাপ))

আপডেট-ডিসেম্বর ২০১১:

  • অবশ্যই, যদি টাইপটির ইতিমধ্যে একটি উত্পাদন সমান () থাকে তবে আপনার অন্য পদ্ধতির প্রয়োজন।
  • আপনি যদি পরীক্ষার উদ্দেশ্যে একচেটিয়া অপরিবর্তনীয় ডেটা স্ট্রাকচারের তুলনা করার জন্য এটি ব্যবহার করে থাকেন তবে আপনার উত্পাদন শ্রেণিতে সমান পরিমাণ যুক্ত করা উচিত নয় (কেউ সমান বাস্তবায়নের জন্য চেঁচিয়ে পরীক্ষা নষ্ট করতে পারে বা আপনি কোনও উত্পাদন-প্রয়োজনীয় সমান বাস্তবায়ন রোধ করতে পারেন) ।

আমি ওভাররাইড নিয়ে সমস্যায় পড়েছি গেটহাসকোড () এর জন্য একটি শালীন ওভাররাইড বাস্তবায়ন করুন (আপনি যখন সমান () এর ওভাররাইড করবেন তখন পুনরায় জিজ্ঞাসা করবেন)।
নাইলিটডাউন

প্রয়োজনটি হ'ল যদি objA.Equals (objB) হয় তবে objA.GetHashCode () == objB.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
এটি হ'ল আগে চেষ্টা করেছিলেন, আপনি অবাক হবেন কতটি অবজেক্ট সিরিয়ালযোগ্য নয় ...
অফার

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

অনুলিপি এবং পেস্টের ত্রুটিগুলি এড়ানোর জন্য আমি পাবলিকআইন্সটেন্স প্রপার্টিএকিয়াল পদ্ধতিতে নিম্নলিখিত লাইনটি যুক্ত করব:

Assert.AreNotSame(self, to);

2

আপনি কি বৈশিষ্ট্যে থাকা আপনার সমস্ত বস্তুর উপরে .TosString () টি ওভাররাইড করবেন? অন্যথায়, যে দ্বিতীয় তুলনা নাল সঙ্গে ফিরে আসতে পারে।

এছাড়াও, দ্বিতীয় দ্বিতীয় তুলনায়, আমি এখন থেকে ছয় মাস / দু'বছর পরে পাঠযোগ্যতার দিক থেকে (এ! = বি) তুলনা করে (এ == বি) নির্মাণ সম্পর্কে বেড়াতে রয়েছি। লাইনটি নিজেই বেশ প্রশস্ত, আপনি যদি একটি প্রশস্ত মনিটর পেয়ে থাকেন তবে ঠিক আছে তবে খুব ভাল প্রিন্ট নাও হতে পারে। (Nitpick)

আপনার সমস্ত বস্তু কি সর্বদা এমন বৈশিষ্ট্য ব্যবহার করে যা এই কোডটি কাজ করবে? কিছু অভ্যন্তরীণ, অ-যথাযথ ডেটা থাকতে পারে যা একের থেকে অন্য বস্তুর কাছে আলাদা হতে পারে, তবে সমস্ত উন্মুক্ত ডেটা একই? আমি এমন কিছু ডেটা নিয়ে ভাবছি যা সময়ের সাথে সাথে পরিবর্তিত হতে পারে যেমন দুটি এলোমেলো সংখ্যার জেনারেটর যা একই সময়ে একই সংখ্যায় হিট হয়, তবে তথ্যের দুটি পৃথক ক্রম তৈরি করতে চলেছে বা এমন কোনও তথ্য যা প্রকাশ পায় না সম্পত্তি ইন্টারফেস মাধ্যমে।


ভাল পয়েন্ট -! = ... সম্মতি, পয়েন্ট নেওয়া। টোস্ট্রিং () চেষ্টা করার চেষ্টা ছিল .গেটভ্যালু কোনও জিনিস ফেরত দেয় (সুতরাং তুলনাটি সর্বদা মিথ্যা, কারণ এটি তুলনামূলকভাবে তুলনামূলকভাবে তুলনামূলক) .. এর চেয়ে ভাল উপায় কি আর আছে?
নাইলিটডাউন

যদি গেটভ্যালু কোনও বস্তু ফিরিয়ে দিচ্ছে, আপনি কি আবার এই ফাংশনটির মাধ্যমে পুনরাবৃত্তি করতে পারেন? অর্থাত, প্রত্যাবর্তিত বস্তুগুলিতে প্রোপার্টি একুয়াল কল করবেন?
এমএমআর

1

আপনি যদি কেবল একই ধরণের অবজেক্টগুলির সাথে তুলনা করছেন বা উত্তরাধিকার শৃঙ্খলে আরও নিচে রেখেছেন তবে কেন বস্তুর পরিবর্তে প্যারামিটারটিকে আপনার বেস টাইপ হিসাবে নির্দিষ্ট করবেন না?

পাশাপাশি প্যারামিটারে নাল চেকও করুন।

তবুও আমি কোডটি আরও পঠনযোগ্য করে তুলতে 'var' ব্যবহার করব (যদি এর সি # 3 কোড থাকে)

এছাড়াও, যদি বস্তুর বৈশিষ্ট্য হিসাবে রেফারেন্সের ধরণ থাকে তবে আপনি কেবল তাদের উপর টোস্ট্রিং () কল করছেন যা প্রকৃত মানগুলির তুলনা করে না। যদি টোস্ট্রিং ওভাররাইড করা না থাকে তবে এটি কেবল টাইপের নামটি স্ট্রিং হিসাবে ফিরিয়ে আনবে যা মিথ্যা-পজিটিভ দিতে পারে।


রেফারেন্সের ধরণের ভাল পয়েন্ট - আমার ক্ষেত্রে এটি কিছু যায় আসে না তবে এটির পক্ষে ভাল সুযোগ রয়েছে।
নাইলিটডাউন

1

আমি প্রথমে যা পরামর্শ দেব তা হ'ল প্রকৃত তুলনাটি ভাগ করা যাতে এটি আরও কিছুটা পাঠযোগ্য (আমি টসস্ট্রিংও বেরিয়েছি) - এটি কি দরকার?):

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

তবে, আমাকে বলতে হবে যে আমি অন্যান্য পোস্টারগুলির সাথে একমত। এটি অলস এবং অদক্ষ গন্ধ করে। পরিবর্তে আপনার আইকোম্যাপারেবল প্রয়োগ করা উচিত :-)।


আমি কেবল আইকাম্পারেবলের দিকে তাকিয়ে ছিলাম তবে মনে হয়েছিল এটি বাছাই ও অর্ডার করার জন্য ছিল .. দুটি বস্তুর সমতার তুলনা করার জন্য এটি কি সত্যই কার্যকর?
নাইলিটডাউন

একেবারে, কারণ .সামগ্রী (অবজেক্ট ও) এটি হিসাবে সংজ্ঞায়িত করা হয়েছে। CompareTo (o) == 0. সুতরাং, সমতা নির্ধারণের জন্য তুলনা তুলনা () সমান করে। এটি প্রতিবিম্ব ব্যবহার করার চেয়ে অনেক বেশি দক্ষ (এবং মান অনুশীলন) হবে।
tsimon

তুলনাটো () এর সাথে সমান সমান প্রয়োগ করা হয়েছে (বা প্রয়োগ করা উচিত) ধরে নিয়ে আমার ভুল হতে পারে। : আপনি যেমন এখানে বর্ণিত সমান অগ্রাহ্য বিবেচনা করা উচিত stackoverflow.com/questions/104158/...
tsimon

1

নাল = নালকে সমান হিসাবে গণ্য করতে এখানে একটি সংশোধিত হয়েছে

 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/compering-the-properties-of-two-objects-via-reflection- এ রেফারেন্স যুক্ত করতে চাই এটির এই পৃষ্ঠায় অন্যান্য উত্তরগুলির চেয়ে আরও সম্পূর্ণ যুক্তি রয়েছে।

তবে আমি তুলনা-নেট-অবজেক্ট লাইব্রেরি https://github.com/GregFinzer/Compare-Net-Objects ( Liviu Trifoi এর উত্তর দ্বারা উল্লেখ করা )
লাইব্রেরিতে নিউগেট প্যাকেজ রয়েছে http://www.nuget.org/packages/ কনফিগার করার জন্য নেটওবজেক্ট এবং একাধিক বিকল্পের তুলনা করুন।


1

নিশ্চিত করুন যে বস্তুগুলি নেই null

থাকা obj1এবং obj2:

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

যদি তারা উভয় শূন্য হয়? তারা কি তাহলে সমান নয়?
এমএমআর

NULLs ভাল পয়েন্ট, আমার ক্ষেত্রে .Equals () ব্যবহার করে কাজ বলে মনে হচ্ছে না, যার কারণে আমি এই সমাধান সঙ্গে আসা পর্যন্ত থাকেন
nailitdown

ভাল, আমি যে ক্ষেত্রে যাচাই করছি সেটি হ'ল দুটি অবজেক্ট, একটি নতুন তৈরি, একটি সেশন থেকে। দুটির সাথে তুলনা করে .কুইলস () মিথ্যা ফিরিয়ে দেয় যদিও উভয়েরই সমান সম্পত্তি মূল্য রয়েছে
নেলিটডাউন

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 এর উত্তরের উপর আপডেট - তুলনাওবজেক্টস।

এটি ইউনিট পরীক্ষায় ভাল কাজ করে:

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

1
এটি দুর্দান্ত যে আপনি নির্বাসনটি ঠিক করেছেন, তবে আমি মনে করি এই উত্তরটি আসলে লিভিউর উত্তরের একটি মন্তব্য হওয়া উচিত। আপনার নমুনা কোড (Liviu এর তুলনায়) বিশেষভাবে কারণ CompareLogic পরামিতি অভাব আছে (যা আমি নিশ্চিত গুরুত্বপূর্ণ নই), এবং এছাড়াও জাহির বার্তা (যা অনুমোদিত নয় এমন একজন ছিলেন)। জোর দিয়ে স্থির করা যেতে পারে: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

@ নওফাল: এর উত্তরে সম্প্রসারণ করতে, আমি আমার ইউনিট পরীক্ষায় বিভিন্ন ধরণের অবজেক্টের সমান সম্পত্তির নাম তুলনা করতে এটি ব্যবহার করি। আমার ক্ষেত্রে ডাটাবেস সত্তা এবং ডিটিও।

আমার পরীক্ষায় এরকম ব্যবহৃত;

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");
    }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.