简体   繁体   中英

c# Object Comparison With Complex Objects

I have a generic object comparison method which I use to compare two models with the same structure.

public static List<Variance> DetailedCompare<T>(this T val1, T val2)
  {
    var variances = new List<Variance>();

    var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach (var property in properties.Where(t => t.IsMarkedWith<IncludeInComparisonAttribute>()))
      {
        var v = new Variance
          {
            PropertyName = property.Name,
            ValA = property.GetValue(val1, null),
            ValB = property.GetValue(val2, null)
          };

          if (v.ValA == null && v.ValB == null) { continue; }

          if (v.ValA != null && !v.ValA.Equals(v.ValB))
            {
              variances.Add(v);
            }
          }
        return variances;
    }

The problem I have is that sometimes an object is passed to it that may contain a list of other objects within it. Because it only compares at the top level it just returns that the object array was changed. Ideally I would like it to go through the nested array and look at the changed values as well.

Ideally I think it should probably make a recursive call when it finds an object array. Any ideas how I might go about this?

Edit - with working examples

Here are some .net fiddle examples of how this is meant to work.

This is the first code example that doesn't search down through the nested objects and just reports that the collection has changed (as per the code above):

https://dotnetfiddle.net/Cng7GI

returns:

Property: NumberOfDesks has changed from '5' to '4' Property: Students has changed from 'System.Collections.Generic.List 1[Student]' to 'System.Collections.Generic.List 1[Student]'

Now if I try and call the DetailedCompare if I find a nested array using:

        if (v.ValA is ICollection)
            {
                Console.WriteLine("I found a nested list");
                variances.AddRange(v.ValA.DetailedCompare(v.ValB));
            } 
        else if(v.ValA != null && !v.ValA.Equals(v.ValB)){
            variances.Add(v);
        }

it doesn't look like the recursive call works

https://dotnetfiddle.net/Ns1tx5

as I just get:

I found a nested list Property: NumberOfDesks has changed from '5' to '4'

If I add:

var list = v.ValA.DetailedCompare<T>(v.ValB);

inside the Collection check, I get an error that:

object does not contain a definition for 'DetailedCompare'... Cannot convert instance argument type 'object' to T

really what I want from it is just a single array of all the property names and their value changes.

Property: NumberOfDesks has changed from '5' to '4'

Property: Id has changed from '1' to '4'

Property: FirstName has changed from 'Cheshire' to 'Door'

etc

Calling the method recursively is the issue here.

If we call a method DetailedCompare recursively passing as parameters two objects all its fine - as we can get their properties and compare them.

However when we call DetailedCompare recursively passing a two list of objects - we can not just get the properties of those lists - but we need to traverse and get the properties of those list and compare their value.

IMHO it would be better to separate the logic using a helper method - so when we find a nested list - we can tackle the logic as I have described above.

This is the Extension class I have written

  public static class Extension
    {
        public static List<Variance> Variances { get; set; }

        static Extension()
        {
            Variances = new List<Variance>();
        }

        public static List<Variance> DetailedCompare<T>(this T val1, T val2)
        {
            var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var property in properties)
            {
                var v = new Variance
                {
                    PropertyName = property.Name,
                    ValA = property.GetValue(val1, null),
                    ValB = property.GetValue(val2, null)
                };

                if (v.ValA == null && v.ValB == null)
                {
                    continue;
                }

                if (v.ValA is ICollection)
                {
                    Console.WriteLine("I found a nested list");
                    DetailedCompareList(v.ValA,v.ValB);
                }
                else if (v.ValA != null && !v.ValA.Equals(v.ValB))
                {
                    Variances.Add(v);
                }
            }

            return Variances;
        }

        private static void DetailedCompareList<T>(T val1, T val2)
        {
            if (val1 is ICollection collection1 && val2 is ICollection collection2)
            {
                var coll1 = collection1.Cast<object>().ToList();
                var coll2 = collection2.Cast<object>().ToList();

                for (int j = 0; j < coll1.Count; j++)
                {
                    Type type = coll1[j].GetType();
                    PropertyInfo[] propertiesOfCollection1 = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                    PropertyInfo[] propertiesOfCollection2 = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

                    for (int i = 0; i < propertiesOfCollection1.Length; i++)
                    {
                        var variance = new Variance
                        {
                            PropertyName = propertiesOfCollection1[i].Name,
                            ValA = propertiesOfCollection1[i].GetValue(coll1[j]),
                            ValB = propertiesOfCollection2[i].GetValue(coll2[j])
                        };

                        if (!variance.ValA.Equals(variance.ValB))
                        {
                            Variances.Add(variance);
                        }
                    }
                }
            }
        }
    }

With the following result: 在此处输入图像描述

Limitations This approach is bounded by the definition of your objects - hence it can only work with 1 level of depth.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM