2013-06-17 32 views
8

Eşleşen nesneler için değiştirilen tüm özelliklerin isimlerini öğrenmek istiyorum. Ben Difference bir URL listesi gibi istiyorumKarşılaştırın Özellikleri otomatik olarak

List<Person> oldPersonList = ... 
List<Person> newPersonList = ... 
List<Difference> = GetDifferences(oldPersonList, newPersonList); 

public List<Difference> GetDifferences(List<Person> oldP, List<Person> newP) { 
    //how to check the properties without casting and checking 
    //for each type and individual property?? 
    //can this be done with Reflection even in Lists?? 
} 

Sonunda:

public enum PersonType { Student, Professor, Employee } 

class Person { 
    public string Name { get; set; } 
    public PersonType Type { get; set; } 
} 

class Student : Person { 
    public string MatriculationNumber { get; set; } 
} 

class Subject { 
    public string Name { get; set; } 
    public int WeeklyHours { get; set; } 
} 

class Professor : Person { 
    public List<Subject> Subjects { get; set; } 
} 

Şimdi Mülkiyet değerleri farklılık nesneleri almak istiyorum: Bunları (basitleştirilmiş) sınıfları Bu:

class Difference { 
    public List<string> ChangedProperties { get; set; } 
    public Person NewPerson { get; set; } 
    public Person OldPerson { get; set; } 
} 

ChangedProperties değişti özelliklerin adını içermelidir.

+0

Listeler için bunu yapmak bir * gerçek * acıdır (add/remove/re-order/etc işlemek zorunda olduğunuz varsayılarak); Ancak, nesne başına temelinde, lütfen bakınız: http://stackoverflow.com/questions/3060382/comparing-2-objects-and-retrieve-a-list-of-fields-with-different-values ​​- hangi yapar? tam olarak bu –

+0

@MarcGravell: Ben denedim ve delta olarak listeler nitelikleri döndürür. Yine de teşekkürler. –

+0

İki nesnede olmayan özellikleri önemsiyorsunuz, I.e. MatriculationNumber bir öğrenciyi bir öğrenciyle karşılaştırdığınızda bir değişiklik olarak kabul edilmeli? –

cevap

0

Bunu kullanarak yapıyorum:

AreEqual sadece Json.NET kullanarak iki nesne seri hale getirilmiş versiyonu karşılaştırır
public bool AreEqual(object leftValue, object rightValue) 
{ 
    var left = JsonConvert.SerializeObject(leftValue); 
    var right = JsonConvert.SerializeObject(rightValue); 

    return left == right; 
} 

public Difference<T> GetDifference<T>(T newItem, T oldItem) 
{ 
    var properties = typeof(T).GetProperties(); 

    var propertyValues = properties 
     .Select(p => new { 
      p.Name, 
      LeftValue = p.GetValue(newItem), 
      RightValue = p.GetValue(oldItem) 
     }); 

    var differences = propertyValues 
     .Where(p => !AreEqual(p.LeftValue, p.RightValue)) 
     .Select(p => p.Name) 
     .ToList(); 

    return new Difference<T> 
    { 
     ChangedProperties = differences, 
     NewItem = newItem, 
     OldItem = oldItem 
    }; 
} 

, bu tutar:

//This structure represents the comparison of one member of an object to the corresponding member of another object. 
    public struct MemberComparison 
    { 
     public static PropertyInfo NullProperty = null; //used for ROOT properties - i dont know their name only that they are changed 

     public readonly MemberInfo Member; //Which member this Comparison compares 
     public readonly object Value1, Value2;//The values of each object's respective member 
     public MemberComparison(PropertyInfo member, object value1, object value2) 
     { 
      Member = member; 
      Value1 = value1; 
      Value2 = value2; 
     } 

     public override string ToString() 
     { 
      return Member.name+ ": " + Value1.ToString() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToString(); 
     } 
    } 

    //This method can be used to get a list of MemberComparison values that represent the fields and/or properties that differ between the two objects. 
    public static List<MemberComparison> ReflectiveCompare<T>(T x, T y) 
    { 
     List<MemberComparison> list = new List<MemberComparison>();//The list to be returned 

     if (x.GetType().IsArray) 
     { 
      Array xArray = x as Array; 
      Array yArray = y as Array; 
      if (xArray.Length != yArray.Length) 
       list.Add(new MemberComparison(MemberComparison.NullProperty, "array", "array")); 
      else 
      { 
       for (int i = 0; i < xArray.Length; i++) 
       { 
        var compare = ReflectiveCompare(xArray.GetValue(i), yArray.GetValue(i)); 
        if (compare.Count > 0) 
         list.AddRange(compare); 
       } 
      } 
     } 
     else 
     { 
      foreach (PropertyInfo m in x.GetType().GetProperties()) 
       //Only look at fields and properties. 
       //This could be changed to include methods, but you'd have to get values to pass to the methods you want to compare 
       if (!m.PropertyType.IsArray && (m.PropertyType == typeof(String) || m.PropertyType == typeof(double) || m.PropertyType == typeof(int) || m.PropertyType == typeof(uint) || m.PropertyType == typeof(float))) 
       { 
        var xValue = m.GetValue(x, null); 
        var yValue = m.GetValue(y, null); 
        if (!object.Equals(yValue, xValue))//Add a new comparison to the list if the value of the member defined on 'x' isn't equal to the value of the member defined on 'y'. 
         list.Add(new MemberComparison(m, yValue, xValue)); 
       } 
       else if (m.PropertyType.IsArray) 
       { 
        Array xArray = m.GetValue(x, null) as Array; 
        Array yArray = m.GetValue(y, null) as Array; 
        if (xArray.Length != yArray.Length) 
         list.Add(new MemberComparison(m, "array", "array")); 
        else 
        { 
         for (int i = 0; i < xArray.Length; i++) 
         { 
          var compare = ReflectiveCompare(xArray.GetValue(i), yArray.GetValue(i)); 
          if (compare.Count > 0) 
           list.AddRange(compare); 
         } 
        } 
       } 
       else if (m.PropertyType.IsClass) 
       { 
        var xValue = m.GetValue(x, null); 
        var yValue = m.GetValue(y, null); 
        if ((xValue == null || yValue == null) && !(yValue == null && xValue == null)) 
         list.Add(new MemberComparison(m, xValue, yValue)); 
        else if (!(xValue == null || yValue == null)) 
        { 
         var compare = ReflectiveCompare(m.GetValue(x, null), m.GetValue(y, null)); 
         if (compare.Count > 0) 
          list.AddRange(compare); 
        } 


       } 
     } 
     return list; 
    } 
+0

Bunu kullandığımda bir * Parametre sayısı uyuşmazlığı * istisnası var. –

3

Biz 2 basit yöntemlerle başlamak referans türleri ve değer türlerini farklı şekilde işlemekten

GetDifference, geçirilen nesnelerdeki özellikleri denetler ve bunları ayrı ayrı karşılaştırır.

farkların listesini almak için: Burada

var oldPersonList = new List<Person> { 
    new Person { Name = "Bill" }, 
    new Person { Name = "Bob" } 
}; 

var newPersonList = new List<Person> { 
    new Person { Name = "Bill" }, 
    new Person { Name = "Bobby" } 
}; 

var diffList = oldPersonList.Zip(newPersonList, GetDifference) 
    .Where(d => d.ChangedProperties.Any()) 
    .ToList(); 
0

Eğer Reflection ile istediğimizi yapar hangi bir kodu var.

public List<Difference> GetDifferences(List<Person> oldP, List<Person> newP) 
    { 
     List<Difference> allDiffs = new List<Difference>(); 
     foreach (Person oldPerson in oldP) 
     { 
      foreach (Person newPerson in newP) 
      { 
       Difference curDiff = GetDifferencesTwoPersons(oldPerson, newPerson); 
       allDiffs.Add(curDiff); 
      } 
     } 

     return allDiffs; 
    } 

    private Difference GetDifferencesTwoPersons(Person OldPerson, Person NewPerson) 
    { 
     MemberInfo[] members = typeof(Person).GetMembers(); 

     Difference returnDiff = new Difference(); 
     returnDiff.NewPerson = NewPerson; 
     returnDiff.OldPerson = OldPerson; 
     returnDiff.ChangedProperties = new List<string>(); 
     foreach (MemberInfo member in members) 
     { 
      if (member.MemberType == MemberTypes.Property) 
      { 
       if (typeof(Person).GetProperty(member.Name).GetValue(NewPerson, null).ToString() != typeof(Person).GetProperty(member.Name).GetValue(OldPerson, null).ToString()) 
       { 
        returnDiff.ChangedProperties.Add(member.Name); 
       } 
      } 
     } 

     return returnDiff; 
    } 
5

Yazılı delegeler kullanarak daha hızlı yansıma tabanlı bir çözüm yazmaya çalışıyorum. Ama sonunda pes ettim ve normal yansımadan daha yüksek performans elde etmek için Marc Gravell'sFast-Member library'a geçtim.

Kodu:

internal class PropertyComparer 
{  
    public static IEnumerable<Difference<T>> GetDifferences<T>(PropertyComparer pc, 
                   IEnumerable<T> oldPersons, 
                   IEnumerable<T> newPersons) 
     where T : Person 
    { 
     Dictionary<string, T> newPersonMap = newPersons.ToDictionary(p => p.Name, p => p); 
     foreach (T op in oldPersons) 
     { 
      // match items from the two lists by the 'Name' property 
      if (newPersonMap.ContainsKey(op.Name)) 
      { 
       T np = newPersonMap[op.Name]; 
       Difference<T> diff = pc.SearchDifferences(op, np); 
       if (diff != null) 
       { 
        yield return diff; 
       } 
      } 
     } 
    } 

    private Difference<T> SearchDifferences<T>(T obj1, T obj2) 
    { 
     CacheObject(obj1); 
     CacheObject(obj2); 
     return SimpleSearch(obj1, obj2); 
    } 

    private Difference<T> SimpleSearch<T>(T obj1, T obj2) 
    { 
     Difference<T> diff = new Difference<T> 
           { 
            ChangedProperties = new List<string>(), 
            OldPerson = obj1, 
            NewPerson = obj2 
           }; 
     ObjectAccessor obj1Getter = ObjectAccessor.Create(obj1); 
     ObjectAccessor obj2Getter = ObjectAccessor.Create(obj2); 
     var propertyList = _propertyCache[obj1.GetType()]; 
     // find the common properties if types differ 
     if (obj1.GetType() != obj2.GetType()) 
     { 
      propertyList = propertyList.Intersect(_propertyCache[obj2.GetType()]).ToList(); 
     } 
     foreach (string propName in propertyList) 
     { 
      // fetch the property value via the ObjectAccessor 
      if (!obj1Getter[propName].Equals(obj2Getter[propName])) 
      { 
       diff.ChangedProperties.Add(propName); 
      } 
     } 
     return diff.ChangedProperties.Count > 0 ? diff : null; 
    } 

    // cache for the expensive reflections calls 
    private Dictionary<Type, List<string>> _propertyCache = new Dictionary<Type, List<string>>(); 
    private void CacheObject<T>(T obj) 
    { 
     if (!_propertyCache.ContainsKey(obj.GetType())) 
     { 
      _propertyCache[obj.GetType()] = new List<string>(); 
      _propertyCache[obj.GetType()].AddRange(obj.GetType().GetProperties().Select(pi => pi.Name)); 
     } 
    } 
} 

Kullanımı:

PropertyComparer pc = new PropertyComparer(); 
var diffs = PropertyComparer.GetDifferences(pc, oldPersonList, newPersonList).ToList(); 

Performansı:

Benim çok önyargılı ölçümler bu yaklaşım yaklaşık 4-6 kat daha hızlı daha olduğunu göstermiştir Json-Dönüşüm ve yaklaşık 9 katı sıradan yansımalar. Fakat adalet içinde, muhtemelen diğer çözümleri biraz hızlandırabilirsiniz.

Sınırlamalar: bireysel Subject öğeleri karşılaştırmak değil örneğin benim çözüm iç içe listeleri üzerinde recurse vermez andan anda

- sadece deneklerin listeleri farklı olduğunu tespit eder, ama ne ya nerede. Ancak, ihtiyacınız olduğunda bu özelliği eklemek çok zor olmamalıdır. En zor kısım muhtemelen Difference sınıfındaki bu farklılıkların nasıl temsil edileceğine karar vermek olacaktır.

1

Herkes her zaman fantezi olmaya çalışır ve bu aşırı genel veri çıkarma yollarını yazar. Bunun bir maliyeti var.

Neden eski okul basit değil.

Bir GetDifferences üye işlevine sahip kişi var. Kalıtılan sınıflarda. Özel özelliklerini geçersiz kılın ve ekleyin. Temel işlevi ekleyin.

KISS - Sakin ol. Yazmak için 10 dakikalık bir maymun çalışması gerekir ve bunun verimli ve işe yaradığını biliyorsunuz.

+1

Bu işe yarayacak. Ama 10 dakika sürmez. Sınıflarım, verdiğim örnekte çok daha karmaşık. En az bir gün, hundrets kod satırları ve bu koddaki olası hatalar ile sonuçlanır. –

+1

Ütünün üzerine ağırlık verdiğinizden emin olun. Düşük sayıda karşılaştırma varsa, yansıma yolu iyidir. Eğer bu yüzbinlerce + üzerinde çalışıyorsanız, o zaman daha ihtiyatlı olurdum. – CodeMonkeyForHire

İlgili konular