之前看到 有递归遍历的,发现 对 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.程序运行如图: