这段时间,小编忙于修改自己所负责的系统中的一个调用接口,这个接口用来调用另外一个系统的API,对方API中的参数进行了升级,在原有对象的属性基础之上,增加了一些新的属性,但是由于对新接口的调用处于测试阶段,需要保证旧的接口仍可用,并且新的接口中传递的对象和旧的接口中传递的对象之间共同的参数值需要一致,但是小编看了看,新旧对象的属性个数都在50+,一个一个对比不是不行,尽管这样做确实提高了准确率,但是这样又回到了原始搬砖的漫漫长路上,因此小编认为这种情况下应该写一个通用的方法,用来对比两个对象之间的属性值。
其实这里和对象的深度复制和浅度复制差不多(这里可以参考小编另外一篇博客,不过使用JAVA实现的:点击打开链接),不过这里小编实现的是对比两个对象之间基础属性的值是否一致,或者是String类型的值是否一致,如果对象中包含复杂对象,则这个方法并不适用,不过有机会可以将此方法改为递归的方式调用,这个小编以后可以想办法实现,这里就先放一个用.NET C#写的这个工具方法吧!
首先声明两个基础类:
public class ClassA
{
public int a { set; get; }
public int? b { set; get; }
public string c { set; get; }
public float d { set; get; }
}
public class ClassB
{
public int a { set; get; }
public int b { set; get; }
public string c { set; get; }
public double d { set; get; }
public decimal e { set; get; }
}
这两个类中,有4个属性的名字是一样的,在这4个属性之中,有两个属性的类型是不一样的,在小编的业务中是这样定义的:
新旧两个对象:
1.同样的属性名对应的属性值应该是一样的,但是不排除这两个属性的类型不同,但是这两个属性的值仍应该一样
如:一个属性是int型,另外一个是String型,但是这两个属性名若是一样的,若其中都是有值得,则里面的内容应该是一样的
如:一个属性是int型,另外一个是String型,但是这两个属性名若是一样的,若其中均没值,如int = 0,String = null,这两个也算值相同
2.由于目前需要保证旧的接口可用,新的接口传递的参数尽管比旧的接口多,但是仍需要包含旧对象中所有的值,一个也不能少
这里小编主要用到了.NET 的反射机制,通过这种方式获取了两个对象的public属性:
通过这种方式获取属性的名字和属性值:
从而进行对象之间基础属性或者String类型的属性值是否一样!
基于以上的需求,小编写了如下的工具方法:
public static bool ContrastTwoObjects(Object a, Object b, bool basic, out string logInfo)
{
logInfo = "";
StringBuilder sb = new StringBuilder();
int count = 0;
if (a == null || b == null)
{
sb.Append(String.Format("第一个对象是否为NULL:{0} ", a == null ? "是" : "否"));
sb.Append(String.Format("第二个对象是否为NULL:{0} ", b == null ? "是" : "否"));
logInfo = sb.ToString();
return false;
}
else
{
PropertyInfo[] propertiesA = a.GetType().GetProperties(
BindingFlags.Instance | BindingFlags.Public);
PropertyInfo[] propertiesB = b.GetType().GetProperties(
BindingFlags.Instance | BindingFlags.Public);
if (propertiesA.Length == 0 || propertiesB.Length == 0)
{
sb.Append(String.Format("第一个对象的属性数量是否为0:{0} ", propertiesA.Length == 0 ? "是" : "否"));
sb.Append(String.Format("第二个对象的属性数量是否为0:{0} ", propertiesB.Length == 0 ? "是" : "否"));
logInfo = sb.ToString();
return false;
}
else
{
//若basic为true,则以第一个传进来的参数为准
//比对第一个参数中的属性是否都出现在第二个参数的属性中,并且这些属性值都是一样的
if (basic)
{
sb.AppendLine("以对象A为基准,比对两个对象共同的属性值是否一致 ");
foreach (PropertyInfo itemA in propertiesA)
{
string nameA = itemA.Name;
object valueA = itemA.GetValue(a, null);
#region 判断A中的属性是否出现在B中,并且属性的值相等
bool findFlag = false;//判断是否有属性名称相同的属性
bool valueFlag = false;//判断属性名称相同的属性的属性值是否一样
foreach (PropertyInfo itemB in propertiesB)
{
string nameB = itemB.Name;
object valueB = itemB.GetValue(b, null);
if (nameA.Equals(nameB))
{
findFlag = true;
//这里只能判断字符串类型的值是否一样
if ((itemA.PropertyType.IsValueType && itemB.PropertyType.FullName == "System.String")
|| (itemB.PropertyType.IsValueType && itemA.PropertyType.FullName == "System.String")
|| (itemA.PropertyType.FullName == "System.String" && itemB.PropertyType.FullName == "System.String"))
{
if ((valueA == null || valueA.ToString() == String.Empty)
&& (valueB == null || valueB.ToString() == String.Empty))
{
valueFlag = true;
break;
}
else
{
if (valueA != null && valueB != null)
{
string s1 = valueA.ToString();
string s2 = valueB.ToString();
if (s1.Equals(s2))
{
valueFlag = true;
break;
}
else
{
sb.Append(
String.Format("第一个对象和第二个对象同样名称的属性值不同,属性名称:{0},属性值1:{1},属性值2:{2} ",
nameA, s1, s2));
valueFlag = false;
break;
}
}
else
{
sb.Append(
String.Format("第一个对象和第二个对象同样名称的属性值不同,属性名称:{0},属性值1:{1},属性值2:{2} ",
nameA, valueA ?? "null", valueB ?? "null"));
valueFlag = false;
break;
}
}
}
//这里只能判断值类型的值是否一样
if (itemA.PropertyType.IsValueType && itemB.PropertyType.IsValueType)
{
//鉴于两个对象同样属性名称不用属性类型的情况,这里将两个基础属性值全部转成字符串类型,从而对比其内容是否一致
string s1 = valueA.ToString();
string s2 = valueB.ToString();
if (s1.Equals(s2))
{
valueFlag = true;
break;
}
else
{
sb.Append(
String.Format("第一个对象和第二个对象同样名称的属性值不同,属性名称:{0},属性值1:{1},属性值2:{2} ",
nameA, s1, s2));
valueFlag = false;
break;
}
}
}
}
#endregion
if (!findFlag)
{
sb.Append(String.Format("未能找到在第二个对象中找到属性值:{0} ", nameA));
logInfo = sb.ToString();
return false;
}
else
{
findFlag = false;
}
if (!valueFlag)
{
logInfo = sb.ToString();
return false;
}
else
{
count++;
valueFlag = false;
}
}
sb.AppendLine(String.Format("属性值一致,供对比属性值数:{0}", count.ToString()));
logInfo = sb.ToString();
return true;
}
//若Basic为false,则只比对两个对象中共同的属性,不以第一个对象为准
else
{
sb.AppendLine("不以A对象为准,比对两个对象共同的属性值是否一致 ");
foreach (PropertyInfo itemA in propertiesA)
{
string nameA = itemA.Name;
object valueA = itemA.GetValue(a, null);
#region 判断A中的属性是否出现在B中,并且属性的值相等
bool valueFlag = false;//判断属性名称相同的属性的属性值是否一样
foreach (PropertyInfo itemB in propertiesB)
{
string nameB = itemB.Name;
object valueB = itemB.GetValue(b, null);
if (nameA.Equals(nameB))
{
//这里只能判断字符串类型的值是否一样
if ((itemA.PropertyType.IsValueType && itemB.PropertyType.FullName == "System.String")
|| (itemB.PropertyType.IsValueType && itemA.PropertyType.FullName == "System.String"))
{
if ((valueA == null || valueA.ToString() == String.Empty)
&& (valueB == null || valueB.ToString() == String.Empty))
{
valueFlag = true;
break;
}
else
{
if (valueA != null && valueB != null)
{
string s1 = valueA.ToString();
string s2 = valueB.ToString();
if (s1.Equals(s2))
{
valueFlag = true;
break;
}
else
{
sb.Append(
String.Format("第一个对象和第二个对象同样名称的属性值不同,属性名称:{0},属性值1:{1},属性值2:{2} ",
nameA, s1, s2));
valueFlag = false;
break;
}
}
else
{
sb.Append(
String.Format("第一个对象和第二个对象同样名称的属性值不同,属性名称:{0},属性值1:{1},属性值2:{2} ",
nameA, valueA ?? "null", valueB ?? "null"));
valueFlag = false;
break;
}
}
}
//这里只能判断值类型的值是否一样
if (itemA.PropertyType.IsValueType && itemB.PropertyType.IsValueType)
{
//鉴于两个对象同样属性名称不用属性类型的情况,这里将两个基础属性值全部转成字符串类型,从而对比其内容是否一致
string s1 = valueA.ToString();
string s2 = valueB.ToString();
if (s1.Equals(s2))
{
valueFlag = true;
break;
}
else
{
sb.Append(
String.Format("第一个对象和第二个对象同样名称的属性值不同,属性名称:{0},属性值1:{1},属性值2:{2} ",
nameA, s1, s2));
valueFlag = false;
break;
}
}
}
}
#endregion
if (!valueFlag)
{
logInfo = sb.ToString();
return false;
}
else
{
count++;
valueFlag = false;
}
}
sb.AppendLine(String.Format("属性值一致,供对比属性值数:{0}", count.ToString()));
logInfo = sb.ToString();
return true;
}
}
}
logInfo = sb.ToString();
return false;
}
这里大家可以看到,返回值用来表明这两个对象是否是一样的,并且其中的一个参数是out类型的,用来像外界输出具体的对比情况,并且小编还提供了另外一个参数,这里有两种情况:
1.以第一个对象为基准,要求第二个对象中需要包含第一个对象中所有的属性名,并且这些属性值需要一致
2.只比对两个对象中共同属性名的属性值
以下是调用的方式和输出的结果:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ReflactionTest
{
public class Program
{
static void Main(string[] args)
{
ClassA a = new ClassA();
ClassB b = new ClassB();
a.a = 1;
a.b = 2;
a.c = "c";
a.d = 0.1f;
b.a = 1;
b.b = 2;
b.d = 0.1d;
b.e = 0.01m;
string info = "";
bool flag = Tool.ContrastTwoObjects(a,b,true,out info);
Console.WriteLine(String.Format("对比结果:{0},结果信息:{1}", flag, info));
}
}
}
这是结果:
这里小编未必能测试到所有的CASE,如果有有缘人和小编遇到了一样的需求,也请多多尝试小编提供的方法,大家共同进步
小编的邮箱是:kameleon@126.com
大家有问题多多交流哈~~~