我有两个类Address和Employee如下:
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
public class Employee
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public Address EmployeeAddress { get; set; }
}
我有两个员工实例如下:
var emp1Address = new Address();
emp1Address.AddressLine1 = "Microsoft Corporation";
emp1Address.AddressLine2 = "One Microsoft Way";
emp1Address.City = "Redmond";
emp1Address.State = "WA";
emp1Address.Zip = "98052-6399";
var emp1 = new Employee();
emp1.FirstName = "Bill";
emp1.LastName = "Gates";
emp1.EmployeeAddress = emp1Address;
var emp2Address = new Address();
emp2Address.AddressLine1 = "Gates Foundation";
emp2Address.AddressLine2 = "One Microsoft Way";
emp2Address.City = "Redmond";
emp2Address.State = "WA";
emp2Address.Zip = "98052-6399";
var emp2 = new Employee();
emp2.FirstName = "Melinda";
emp2.LastName = "Gates";
emp2.EmployeeAddress = emp2Address;
现在我该如何编写一个方法来比较这两个雇员并返回具有不同值的属性列表。所以在这个例子中我希望结果是FirstName和Address.AddressLine1。
答案 0 :(得分:13)
public static List<PropertyInfo> GetDifferences(Employee test1, Employee test2)
{
List<PropertyInfo> differences = new List<PropertyInfo>();
foreach (PropertyInfo property in test1.GetType().GetProperties())
{
object value1 = property.GetValue(test1, null);
object value2 = property.GetValue(test2, null);
if (!value1.Equals(value2))
{
differences.Add(property);
}
}
return differences;
}
答案 1 :(得分:2)
您不一定需要反射来执行比较。您可以编写一个比较器类,它接受两个Employee或Address实例,并比较每个应匹配的字段。对于任何不匹配的元素,您可以将字符串(或PropertyInfo
)元素添加到某个列表以返回给调用者。
是否返回PropertyInfo
,MemberInfo
或仅返回字符串取决于调用者需要对结果执行的操作。如果您确实需要访问包含差异的字段,PropertyInfo/MemberInfo
可能会更好 - 但只是报告差异,字符串就足够了。
反射的主要价值在于编写一个通用对象比较器,它可以采用任何类型的对象的两个实例并比较它们的公共字段和属性。这有助于避免反复编写重复比较代码 - 但这似乎不是你所处的情况。
答案 2 :(得分:2)
这是基于Oskar Kjellin的awnser的通用递归解决方案。
我也以gist的形式发布了此代码,因此您可以检查最新版本或将其星标/克隆/分叉:)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
protected List<KeyValuePair<Type, PropertyInfo>> RecrusiveReflectionCompare<T>(T first, T second)
where T : class
{
var differences = new List<KeyValuePair<Type, PropertyInfo>>();
var parentType = first.GetType();
void CompareObject(object obj1, object obj2, PropertyInfo info)
{
if (!obj1.Equals(obj2))
{
differences.Add(new KeyValuePair<Type, PropertyInfo>(parentType, info));
}
}
foreach (PropertyInfo property in parentType.GetProperties())
{
object value1 = property.GetValue(first, null);
object value2 = property.GetValue(second, null);
if (property.PropertyType == typeof(string))
{
if (string.IsNullOrEmpty(value1 as string) != string.IsNullOrEmpty(value2 as string))
{
CompareObject(value1, value2, property);
}
}
else if (property.PropertyType.IsPrimitive)
{
CompareObject(value1, value2, property);
}
else
{
if (value1 == null && value2 == null)
{
continue;
}
differences.Concat(RecrusiveReflectionCompare(value1, value2));
}
}
return differences;
}
答案 3 :(得分:0)
无需反思。当然,这个例子是返回一个带有属性名称的字符串...如果你需要实际的PropertyInfo
对象,事情会变得更加困难,但不会太多。
public static IEnumerable<string> DiffEmployees
(Employee one, Employee two)
{
if(one.FirstName != two.FirstName)
yield return "FirstName";
if(one.LastName != two.LastName)
yield return "LastName";
if(one.Address.AddressLine1 != two.Address.AddressLine1)
yield return "Address.AddressLine1";
// And so on.
}
答案 4 :(得分:0)
public IEnumerable<PropertyInfo> GetNotEqualsProperties(Employee emp1, Employee emp2)
{
Type employeeType = typeof (Employee);
var properies = employeeType.GetProperties();
foreach (var property in properies)
if(!property.GetValue(emp1, null).Equals(property.GetValue(emp2, null))) //TODO: check for null
yield return property;
}
对于复杂属性,您必须重写Equals方法
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public override bool Equals(object obj)
{
if (obj as Address == null)
return false;
return ((Address) obj).AddressLine1.Equals(AddressLine1);
}
}