2017-03-15 24 views
4

2つのオブジェクト(同じ型の)をとり、異なる値を持つプロパティのリストを返す汎用メソッドを作成する必要があります。 私の要件は少し違っているので、私はこれを重複しているとは思わない。c#異なる値を持つプロパティの2つのオブジェクトを比較します。

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を使用してこれを達成できますか?

+0

何かが変更されたときは常に、「dirty」フラグを設定することをお勧めします。オブジェクト全体を比較するよりもはるかに高速です(しかも簡単です)。 – Psi

+1

[型のプロパティ](http://stackoverflow.com/q/737151/1997232)を列挙して、両方のインスタンスから値を取得し、それらの名前を比較して返すことができます。 – Sinatr

+1

@Psiいいえ、これらのオブジェクトは私がそれを制御できないいくつかのAPIメソッドによって変更されるので、私はそれを行うことはできません。 – Rahul

答えて

1
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; 
     } 
    } 
} 
+0

それは#1で失敗しますが、そうでなければ良いです:) – john

+0

ごめんなさい、 –

+0

"同一の"クラスを作成し、それをPersonのプロパティとして追加します。 GetHashcode()等価チェックに失敗するため、値がtrueの場合でもfalseを返します。 – john

1

これを試してください。どのクラスでも一般的でなければなりません。

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(); 
     } 
    } 
2

私は上記の小さな一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() - 拡張メソッドです。

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

これは私が思いつく最も簡単な解決策でした。ジェネリック、パフォーマンス、シンプル。ただし、前提条件は、いずれかのプロパティがクラスオブジェクトである場合、等価を正しく実装する必要があることです。

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); 
    } 
} 
関連する問題