判断两个相同引用类型的对象的属性值是否相等,增加对List,Dictionary的支持

83 篇文章 3 订阅

之前看到 有递归遍历的,发现 对 List,Dictionary会出现【个数不一致的】异常。

这里增加对列表,字典的支持 与解析

新建控制台应用程序CompareObjectDemo,选择.NET4.6.1

使用反射来进行处理

1.新建关键类ComparePropertyUtil.cs

ComparePropertyUtil类的源代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace CompareObjectDemo
{
    /// <summary>
    /// 判断两个相同引用类型的对象的属性值是否相等
    /// 记录登录用户对配置界面的更新
    /// 只考虑字段Field和属性Property
    /// </summary>
    public class ComparePropertyUtil
    {
        /// <summary>
        /// 判断两个相同引用类型的对象的属性值是否相等
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj1">对象1:上一个对象</param>
        /// <param name="obj2">对象2:当前对象</param>
        /// <param name="type">按type类型中的属性进行比较</param>
        /// <param name="paraName">成员变量时,字段或属性的名称</param>
        /// <param name="descriptionNameMember">成员变量时,字段和属性的具体描述特性</param>
        /// <returns></returns>
        public static bool ComparePropertiesValueSame<T>(T obj1, T obj2, Type type, string paraName = "", string descriptionNameMember = "")
        {
            string user = "张三";//修改用户名
            if (type == null)
            {
                return false;
            }
            bool isSame = true;//是否完全一致
            string descriptionName = descriptionNameMember;
            if (string.IsNullOrEmpty(descriptionName))
            {
                DescriptionAttribute descriptionAttr = type.GetCustomAttribute<DescriptionAttribute>();
                descriptionName = descriptionAttr?.Description;
            }
            //为空判断
            if (obj1 == null && obj2 == null)
            {
                return true;
            }
            else if (obj1 == null && obj2 != null)
            {
                //只有一个为空
                SaveParameterLogAndCsv(paraName, descriptionName, Convert.ToString(obj1), Convert.ToString(obj2), user);
                return false;
            }
            else if (obj1 != null && obj2 == null)
            {
                //只有一个为空
                SaveParameterLogAndCsv(paraName, descriptionName, Convert.ToString(obj1), Convert.ToString(obj2), user);
                return false;
            }
            else if (IsCanCompare(type))
            {
                if (!obj1.Equals(obj2))
                {
                    //值类型或者是字符串时,如果实际值不一致,就记录更新记录
                    SaveParameterLogAndCsv(type.Name, descriptionName, Convert.ToString(obj1), Convert.ToString(obj2), user);
                    return false;
                }
                return true;
            }
            else if (typeof(IList).IsAssignableFrom(type))
            {
                //当前属性类型是否继承于【列表】IList,List<T>和Array都继承于IList
                IList tempObj1 = obj1 as IList;//此时obj1,obj2一定不为null
                IList tempObj2 = obj2 as IList;
                if (tempObj1.Count != tempObj2.Count)
                {
                    //保存更新
                    SaveParameterLogAndCsv(type.Name, descriptionName, $"集合个数{tempObj1.Count}", $"集合个数{tempObj2.Count}", user);
                    return false;
                }
                //集合格式一致,就遍历每一个元素
                for (int i = 0; i < tempObj1.Count; i++)
                {
                    bool isSameTemp = ComparePropertiesValueSame(tempObj1[i], tempObj2[i], tempObj1[i].GetType());
                    if (!isSameTemp)
                    {
                        isSame = false;//如果存在某一个属性值被更新,就认为是更新了
                    }
                }
                return isSame;
            }
            else if (typeof(IDictionary).IsAssignableFrom(type))
            {
                //当前属性类型是否继承于【键值对字典】IDictionary,Dcitionary和Hashtable都继承于IDictionary
                IDictionary tempObj1 = obj1 as IDictionary;//此时obj1,obj2一定不为null
                IDictionary tempObj2 = obj2 as IDictionary;
                if (tempObj1.Count != tempObj2.Count)
                {
                    //保存到表格中,按天存。
                    SaveParameterLogAndCsv(type.Name, descriptionName, $"集合个数{tempObj1.Count}", $"集合个数{tempObj2.Count}", user);
                    return false;
                }
                //集合格式一致,就遍历每一个元素
                IDictionaryEnumerator enu1 = tempObj1.GetEnumerator();
                IDictionaryEnumerator enu2 = tempObj2.GetEnumerator();
                //集合格式一致,就遍历每一个元素
                for (int i = 0; i < tempObj1.Count; i++)
                {
                    enu1.MoveNext();
                    enu2.MoveNext();
                    bool isSameTempKey = ComparePropertiesValueSame(enu1.Key, enu2.Key, enu1.Key.GetType());
                    bool isSameTempValue = ComparePropertiesValueSame(enu1.Value, enu2.Value, enu1.Value.GetType());
                    if (!isSameTempKey || !isSameTempValue)
                    {
                        isSame = false;//如果存在某一个属性值被更新,就认为是更新了
                    }
                }
                return isSame;
            }
            else if (typeof(IEnumerable).IsAssignableFrom(type)) //对集合进行特殊处理
            {
                //当前属性类型是否继承于【枚举数集合】IEnumerable,HashSet、Queue和Stack都继承于IEnumerable
                IEnumerable tempObj1 = obj1 as IEnumerable;//此时obj1,obj2一定不为null
                IEnumerable tempObj2 = obj2 as IEnumerable;
                //集合格式一致,就遍历每一个元素
                IEnumerator enu1 = tempObj1.GetEnumerator();
                IEnumerator enu2 = tempObj2.GetEnumerator();
                //集合格式一致,就遍历每一个元素
                while (enu1.MoveNext() && enu2.MoveNext())
                {
                    bool isSameTemp = ComparePropertiesValueSame(enu1.Current, enu2.Current, enu1.Current.GetType());
                    if (!isSameTemp)
                    {
                        isSame = false;//如果存在某一个属性值被更新,就认为是更新了
                    }
                }
                //如果结尾的时候,某一个集合仍然有值,说明集合个数不一致
                if(enu1.MoveNext() || enu2.MoveNext())
                {
                    SaveParameterLogAndCsv(type.Name, descriptionName, $"{enu1.Current}", $"{enu2.Current}", user);
                    return false;
                }
                return isSame;
            }
            //如果是自定义类CustomClass
            //遍历出当前类型的所有公共字段 和 公共属性
            MemberInfo[] memberInfos = type.GetMembers();
            foreach (MemberInfo po in memberInfos)
            {
                if (po.MemberType != MemberTypes.Field && po.MemberType != MemberTypes.Property)
                {
                    //只考虑字段和属性,不考虑构造函数、方法、事件等
                    continue;
                }
                object oldVal = null;
                object newVal = null;
                Type objType = null;
                if (po is PropertyInfo pi)
                {
                    objType = pi.PropertyType;//属性类型
                    oldVal = pi.GetValue(obj1);//旧值
                    newVal = pi.GetValue(obj2);//新值 
                }
                else if (po is FieldInfo fi)
                {
                    objType = fi.FieldType;//字段类型
                    oldVal = fi.GetValue(obj1);//旧值
                    newVal = fi.GetValue(obj2);//新值 
                }
                DescriptionAttribute descriptionAttribute = po.GetCustomAttribute<DescriptionAttribute>(false);
                descriptionNameMember = descriptionAttribute?.Description;//修改的参数
                if (oldVal == null && newVal == null)
                {
                    //仍是相等 初始值都为null
                    continue;
                }
                bool isSameTemp = ComparePropertiesValueSame(oldVal, newVal, objType, po.Name, descriptionNameMember);
                if (!isSameTemp)
                {
                    isSame = false;//如果存在某一个属性值被更新,就认为是更新了
                }
            }
            return isSame;
        }

        /// <summary>
        /// 保存参数修改日志与Csv
        /// </summary>
        /// <param name="paraName"></param>
        /// <param name="descriptionName"></param>
        /// <param name="oldValue"></param>
        /// <param name="newValue"></param>
        /// <param name="userName"></param>
        public static void SaveParameterLogAndCsv(string paraName, string descriptionName, string oldValue, string newValue, string userName)
        {
            //保存到表格中,按天存。
            DateTime dtNow = DateTime.Now;
            Console.WriteLine($"配置参数值已更新,更新的参数为:【{paraName}】,参数描述【{descriptionName}】,旧值【{oldValue}】,新值【{newValue}】,更新人【{userName}】,更新时间【{dtNow.ToString("HH:mm:ss.fff")}】");
        }

        /// <summary>
        /// 该类型是否可直接进行值的比较
        /// 所有的结构Struct【比如int,double,DateTime】,枚举Enum 都是值类型,字符串也是可以比较的
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        private static bool IsCanCompare(Type t)
        {
            //所有的结构Struct【比如int,double,DateTime】,枚举Enum 都是值类型
            if (t.IsValueType)
            {
                return true;
            }
            if (t.FullName == typeof(string).FullName)
            {
                return true;
            }
            return false;
        }
    }
}

2.新建测试类InstanceTest.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace CompareObjectDemo
{
    /// <summary>
    /// 实例化测试类
    /// </summary>
    public class InstanceTest 
    {
        public InstanceTest()
        {
            this.Name = "未配置的接口";
            this.OnlineFlag = false;
            ListExtensionParam = new List<ExtensionParam>();
        }
        /// <summary>
        /// 接口名称
        /// </summary>
        [Description("接口名称")]
        public string Name { get; set; }
        /// <summary>
        /// 是否在线,是否启用。不启用【false】时,不调用接口,直接按OK处理
        /// </summary>
        [Description("是否启用")]
        public bool OnlineFlag { get; set; }
        /// <summary>
        /// Url地址
        /// </summary>
        [Description("Url地址")]
        public string Url { get; set; }
        /// <summary>
        /// 发送超时
        /// </summary>
        [Description("发送超时")]
        public int SendTimeout { get; set; }
        /// <summary>
        /// 接收超时
        /// </summary>
        [Description("接收超时")]
        public int RecvTimeout { get; set; }
        /// <summary>
        /// 参数集合
        /// </summary>
        [Description("参数集合")]
        public List<ExtensionParam> ListExtensionParam = new List<ExtensionParam>();
    }
    public class ExtensionParam
    {
        /// <summary>
        /// MES参数名称
        /// </summary>
        [Description("键名")]
        public string Key { get; set; }
        /// <summary>
        /// 配置的具体值
        /// </summary>
        [Description("具体值")]
        public string Val { get; set; }
        /// <summary>
        /// 参数描述
        /// </summary>
        [Description("描述")]
        public string Des { get; set; }

        [Description("测试")]
        public Test test { get; set; }
    }

    public class Test
    {
        [Description("编号")]
        public int Id { get; set; }
        [Description("测试姓名")]
        public string NameTest { get; set; }
    }
}

3.测试Program.cs如下

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

namespace CompareObjectDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.SetWindowSize(140, 40);
            InstanceTest last = new InstanceTest();
            last.Name = "ABCD";
            last.RecvTimeout = 3000;
            last.ListExtensionParam.Add(new ExtensionParam()
            {
                Key = "site",
                Val = "2002",
                Des = "站点",
            });
            last.ListExtensionParam.Add(new ExtensionParam()
            {
                Key = "operation",
                Val = "ASM001",
                Des = "操作",
                test = new Test() { Id = 8, NameTest = "ZZZ3333" }
            });

            InstanceTest current = new InstanceTest();
            current.Name = "1234";
            current.Url = "xxxx";
            current.ListExtensionParam.Add(new ExtensionParam()
            {
                Key = "site",
                Val = "2019",
                Des = "站点",
                test = new Test() { Id = 1, NameTest = "XXX111" }
            });
            current.ListExtensionParam.Add(new ExtensionParam()
            {
                Key = "operation",
                Val = "Hello",
                Des = "操作",
                test = new Test() { Id = 2, NameTest = "YYY222" }
            });

            ComparePropertyUtil.ComparePropertiesValueSame(last, current, typeof(InstanceTest));

            Console.WriteLine("------------比较基础数据类型------------");
            int oldValue = sizeof(short);
            int newValue = sizeof(decimal);
            ComparePropertyUtil.ComparePropertiesValueSame(oldValue, newValue, typeof(int));
            Console.ReadLine();
        }
    }
}

4.程序运行如图:

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斯内科

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值