简体   繁体   English

使用反射比较对象属性

[英]Comparing Object properties using reflection

I have two classes Address and Employee as follows: 我有两个类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; }
    }

I have two employee instances as follows: 我有两个员工实例如下:

    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;

Now how can I write a method which compares these two employees and returns the list of properties which have different values. 现在,我该如何编写一个方法来比较这两个雇员并返回具有不同值的属性列表。 So in this example I would like the result to be FirstName and Address.AddressLine1 . 所以在这个例子中我希望结果是FirstName和Address.AddressLine1。

Like LBushskin said, you do not have to do this. 就像LBushskin说的那样,你不必这样做。 This is not the fastest way! 这不是最快的方法! Buy if you want, try this: 如果你想购买,试试这个:

    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;
    }

You don't necessarily need reflection to perform the comparison. 您不一定需要反射来执行比较。 You can write a comparer class that takes two instances of Employee or Address, and compares each field that should match. 您可以编写一个比较器类,它接受两个Employee或Address实例,并比较每个应匹配的字段。 For any that don't match, you can add a string (or PropertyInfo ) element to some list to return to the caller. 对于任何不匹配的元素,您可以将字符串(或PropertyInfo )元素添加到某个列表以返回给调用者。

Whether you return a PropertyInfo , MemberInfo , or just a string depends on what the caller needs to do with the result. 是返回PropertyInfoMemberInfo还是仅返回字符串取决于调用者需要对结果执行的操作。 If you actually need to visit the fields that contain differences, the PropertyInfo/MemberInfo may be better - but to just report the differences a string is probaby sufficient. 如果您确实需要访问包含差异的字段, PropertyInfo/MemberInfo可能会更好 - 但只是报告差异,字符串就足够了。

The main value of reflection would be to write a general purpose object comparer that could take two instances of any kind of object and compare their public fields and properties. 反射的主要价值在于编写一个通用对象比较器,它可以接受任何类型对象的两个实例并比较它们的公共字段和属性。 This helps avoid writing repetetive comparison code over and over - but that doesn't seem like the case you're in. 这有助于避免反复编写重复比较代码 - 但这似乎不是你所处的情况。

Here is a generic and recursive solution based on Oskar Kjellin's awnser . 这是一个基于Oskar Kjellin的awnser的通用和递归解决方案。

I have posted this code as gist as well, so you can check the latest version or star/clone/fork it :) 我已将此代码发布为gist ,因此您可以查看最新版本或star / clone / fork :)

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;
    }

No need for reflection. 不需要反思。 Of course, this example is returning a string with the property names...if you need the actual PropertyInfo object, things would get a little more difficult, but not by much. 当然,这个例子是返回一个带有属性名称的字符串......如果你需要实际的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.
}
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;
}

And for complex properties you have to override Equals method 对于复杂属性,您必须重写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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM