检查两个复杂对象的子等式

时间:2018-04-16 03:02:09

标签: c#

假设我有一个类如下:

public class A
     {
        public string prop1 { get; set; }
        public List<B> prop2 { get; set; }  //B is a user-defined type
        // and so on
     }

现在假设我有两个Aa1a2类型的对象。但并非所有属性都已初始化。我需要检查a1是否对a2的所有非空属性具有相同的值(我不知道这个术语是什么,可能是“次等式”?)。< / p>

我要做的是为Equals()中使用的所有类型覆盖A方法,然后迭代a2的{​​{1}}属性,并检查相应属性的相等性(如果它是List那么我应该使用HashSetSetEquals())。

是否有更好的(更高效,更少的代码等)方式?

1 个答案:

答案 0 :(得分:1)

有几种技术,但这里有一些东西可以开始。我已经使用了一个扩展方法,它一直工作到object(为什么不),但是你可以将它限制在更高级别,使它成为你的基类本身的一部分。

请注意,反射相对较贵,因此如果您经常进行某种操作,则应考虑缓存相关的PropertyInfos。

namespace ConsoleApp1
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Reflection;

    public class BaseClass<T>
    {
        public string prop1
        {
            get;
            set;
        }

        public List<T> prop2
        {
            get;
            set;
        } //B is a user-defined type
    }

    public class DescendentClass<T> : BaseClass<T>
    {
    }

    public static class DeepComparer
    {
        public static bool IsDeeplyEquivalent(this object current, object other)
        {
            if (current.Equals(other)) return true;
            Type currentType = current.GetType();
            // Assumption, cannot be equivalent if another class (descendent classes??)     
            if (currentType != other.GetType())
            {
                return false;
            }

            foreach (PropertyInfo propertyInfo in currentType.GetProperties())
            {
                object currentValue = propertyInfo.GetValue(current, null);
                object otherValue = propertyInfo.GetValue(other, null);
                // Assumption, nulls for a given property are considered equivalent
                if (currentValue == null && otherValue == null)
                {
                    continue;
                }

                // One is null, the other isn't so are not equal
                if (currentValue == null || otherValue == null)
                {
                    return false;
                }

                ICollection currentCollection = currentValue as ICollection;
                if (currentCollection == null)
                {
                    // Not a collection, just check equality
                    if (!currentValue.Equals(otherValue))
                    {
                        return false;
                    }
                }
                else
                {
                    // Collection, not interested whether list/array/etc are same object, just that they contain equal elements
                    // questioner guaranteed that all elements are unique               
                    HashSet<object> elements = new HashSet<object>();
                    foreach (object o in currentCollection)
                    {
                        elements.Add(o);
                    }

                    List<object> otherElements = new List<object>();
                    foreach (object o in (ICollection)otherValue)
                    {
                        otherElements.Add(o);
                    }

                    // cast below can be safely made because we have already asserted that
                    // current and other are the same type
                    if (!elements.SetEquals(otherElements))
                    {
                        return false;
                    }
                }
            }

            return true;
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            BaseClass<int> a1 = new BaseClass<int>{prop1 = "Foo", prop2 = new List<int>{1, 2, 3}};
            BaseClass<int> a2 = new BaseClass<int>{prop1 = "Foo", prop2 = new List<int>{2, 1, 3}};
            BaseClass<int> a3 = new BaseClass<int>{prop1 = "Bar", prop2 = new List<int>{2, 1, 3}};
            BaseClass<string> a4 = new BaseClass<string>{prop1 = "Bar", prop2 = new List<string>()};
            BaseClass<int> a5 = new BaseClass<int>{prop1 = "Bar", prop2 = new List<int>{1, 3}};
            DateTime d1 = DateTime.Today;
            DateTime d2 = DateTime.Today;
            List<string> s1 = new List<string>{"s1", "s2"};
            string[] s2 = {"s1", "s2"};
            BaseClass<DayOfWeek> b1 = new BaseClass<DayOfWeek>{prop1 = "Bar", prop2 = new List<DayOfWeek>{DayOfWeek.Monday, DayOfWeek.Tuesday}};
            DescendentClass<DayOfWeek> b2 = new DescendentClass<DayOfWeek>{prop1 = "Bar", prop2 = new List<DayOfWeek>{DayOfWeek.Monday, DayOfWeek.Tuesday}};
            Console.WriteLine("a1 == a2 : " + a1.IsDeeplyEquivalent(a2)); // true, different order ignored
            Console.WriteLine("a1 == a3 : " + a1.IsDeeplyEquivalent(a3)); // false, different prop1
            Console.WriteLine("a3 == a4 : " + a3.IsDeeplyEquivalent(a4)); // false, different types
            Console.WriteLine("a3 == a5 : " + a3.IsDeeplyEquivalent(a5)); // false, difference in list elements
            Console.WriteLine("d1 == d2 : " + d1.IsDeeplyEquivalent(d2)); // true
            Console.WriteLine("s1 == s2 : " + s1.IsDeeplyEquivalent(s2)); // false, different types
            Console.WriteLine("b1 == b2 : " + s1.IsDeeplyEquivalent(s2)); // false, different types
        Console.WriteLine("b1 == b1 : " + b1.IsDeeplyEquivalent(b1)); // true, same object
            Console.ReadLine();
        }
    }
}