【问题标题】:Compare two objects for properties with different values比较具有不同值的属性的两个对象
【发布时间】:2017-08-06 04:30:06
【问题描述】:

我需要创建一个泛型方法,它将采用两个对象(相同类型),并返回具有不同值的属性列表。 由于我的要求有点不同,我不认为这是重复的。

public class Person
{
   public string Name {get;set;}
   public string Age {get;set;}
}

Person p1 = new Person{FirstName = "David", Age = 33}
Person p2 = new Person{FirstName = "David", Age = 44}

var changedProperties = GetChangedProperties(p1,p2);

代码说明需求:

public List<string> GetChangedProperties(object A, object B)
{
    List<string> changedProperties = new List<string>();
   //Compare for changed values in properties 
   if(A.Age != B.Age)
   {
       //changedProperties.Add("Age");
   } 
   //Compare other properties
   ..
   ..
   return changedProperties;
}

应考虑以下事项:

  1. 通用 - 应该能够比较任何类型的对象(具有相同的类)
  2. 性能
  3. 简单

那里有现成可用的库吗?

我可以使用AutoMapper 实现此目的吗?

【问题讨论】:

  • 我建议在发生变化时设置dirty-Flag。它比比较整个对象要快得多(也更容易)。
  • 您可以枚举properties of the type,从两个实例中获取值,比较并返回不同的名称。
  • @Psi 不,我不能这样做,因为这些对象被一些我无法控制的 API 方法更改。
  • @Sinatr 我不确定比较大对象时的性能。所以我正在寻找一些替代品。
  • @Rahul 如果您想要一个可以与您无法控制的任何类一起使用的方法,那么您必须使用反射,它会影响性能。另一种方法是编写特定于要比较的类或类的代码。

标签: c# object compare comparison automapper


【解决方案1】:

我在Krishnas answer上进步了一点:

public List<string> GetChangedProperties<T>(object A, object B)
{
    if (A != null && B != null)
    {
        var type = typeof(T);
        var allProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        var allSimpleProperties = allProperties.Where(pi => pi.PropertyType.IsSimpleType());
        var unequalProperties =
               from pi in allSimpleProperties
               let AValue = type.GetProperty(pi.Name).GetValue(A, null)
               let BValue = type.GetProperty(pi.Name).GetValue(B, null)
               where AValue != BValue && (AValue == null || !AValue.Equals(BValue))
               select pi.Name;
        return unequalProperties.ToList();
    }
    else
    {
        throw new ArgumentNullException("You need to provide 2 non-null objects");
    }
}

因为它不适合我。这个可以,你需要让它工作的唯一另一件事是我从this answer 改编而来的 IsSimpleType()-Extension 方法(我只将它转换为扩展方法)。

public static bool IsSimpleType(this Type type)
{
    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
    {
        // nullable type, check if the nested type is simple.
        return type.GetGenericArguments()[0].IsSimpleType();
    }
    return type.IsPrimitive
      || type.IsEnum
      || type.Equals(typeof(string))
      || type.Equals(typeof(decimal));
}

【讨论】:

    【解决方案2】:

    试试这个。对于任何类都应该是通用的。

     public List<string> GetChangedProperties(object A, object B)
        {
           if (A!= null && B != null)
            {
                var type = typeof(T);
             var unequalProperties =
                    from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    where pi.GetUnderlyingType().IsSimpleType() && pi.GetIndexParameters().Length == 0
                    let AValue = type.GetProperty(pi.Name).GetValue(A, null)
                    let BValue = type.GetProperty(pi.Name).GetValue(B, null)
                    where AValue != BValue && (AValue == null || !AValue.Equals(BValue))
                    select pi.Name;
         return unequalProperties.ToList();
             }
        }
    

    【讨论】:

      【解决方案3】:
      using System;
      using System.Collections.Generic;
      using System.Reflection;
      
      namespace ConsoleApplication2
      {
          class Program
          {
              static void Main(string[] args)
              {
                  Person p1 = new Person("David", 33);
                  Person p2 = new Person("David", 44);
      
                  var changedProperties = GetChangedProperties(p1, p2);
              }
      
              public class Person
              {
                  public Person(string name, int age)
                  {
                      this.name = name;
                      this.age = age;
                  }
      
                  public int age { get; set; }
                  public string name { get; set; }
              }
      
              public static List<string> GetChangedProperties(Object A, Object B)
              {
                  if (A.GetType() != B.GetType())
                  {
                      throw new System.InvalidOperationException("Objects of different Type");
                  }
                  List<string> changedProperties = ElaborateChangedProperties(A.GetType().GetProperties(), B.GetType().GetProperties(), A, B);
                  return changedProperties;
              }
      
      
              public static List<string> ElaborateChangedProperties(PropertyInfo[] pA, PropertyInfo[] pB, Object A, Object B)
              {
                  List<string> changedProperties = new List<string>();
                  foreach (PropertyInfo info in pA)
                  {
                      object propValueA = info.GetValue(A, null);
                      object propValueB = info.GetValue(B, null);
                      if (propValueA != propValueB)
                      {
                          changedProperties.Add(info.Name);
                      }
                  }
                  return changedProperties;
              }
          }
      }
      

      【讨论】:

      • 对不起,我不明白,你是什么意思?
      • 创建一个“相同”的类,并将其添加为 Person 的属性。即使它的值为真,它也会返回假,因为它将无法通过 GetHashcode() 相等性检查。
      • 但是该方法无论如何都不接受不同的对象类型。我现在添加了一个异常控制以便更好地练习^^
      【解决方案4】:

      这是我能想到的最简单的解决方案。通用、高性能和简单。但是,前提是如果任何属性是类对象,它们必须正确实现等于。

      public List<string> GetChangedProperties<T>(T a, T b) where T:class
      {   
          if (a != null && b != null)
          {
              if (object.Equals(a, b))
              {
                  return new List<string>();
              }
              var allProperties = a.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
              return allProperties.Where(p => !object.Equals( p.GetValue(a),p.GetValue(b))).Select(p => p.Name).ToList();
          }
          else
          {
              var aText = $"{(a == null ? ("\"" + nameof(a) + "\"" + " was null") : "")}";
              var bText = $"{(b == null ? ("\"" + nameof(b) + "\"" + " was null") : "")}";
              var bothNull = !string.IsNullOrEmpty(aText) && !string.IsNullOrEmpty(bText);
              throw new ArgumentNullException(aText + (bothNull ? ", ":"" )+ bText );
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-08
        • 2016-09-15
        • 2010-12-16
        • 1970-01-01
        • 1970-01-01
        • 2021-03-01
        • 2022-10-16
        相关资源
        最近更新 更多