2013-06-17 9 views
8

मैं मिलान करने वाली वस्तुओं के लिए परिवर्तित होने वाले सभी गुणों के नाम प्राप्त करना चाहता हूं।स्वचालित रूप से गुणों की तुलना करें

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

अब मैं वस्तुओं के लिए जहां संपत्ति मूल्यों अलग प्राप्त करना चाहते हैं: मैं इन (सरलीकृत) वर्ग

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

अंत में मैं जैसे Difference रों की एक सूची है चाहते हैं इस:

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

ChangedProperties बदल गुण का नाम शामिल करना चाहिए।

+0

सूचियों के लिए ऐसा करना एक * वास्तविक * दर्द है (माना जाता है कि आपको जोड़ने/निकालने/पुन: आदेश/आदि को संभालने की आवश्यकता है); हालांकि, प्रति-ऑब्जेक्ट आधार पर, कृपया देखें: http://stackoverflow.com/questions/3060382/comparing-2-objects-and-retrieve-a-list-of-fields-with- अलग-अलग - जो करता है वास्तव में यह –

+0

@MarcGravell: मैंने इसे आजमाया और यह उन गुणों को लौटाता है जो डेल्टा के रूप में सूचीबद्ध हैं। फिर भी धन्यवाद। –

+0

क्या आप उन संपत्तियों की परवाह करते हैं जो दोनों वस्तुओं में नहीं हैं, आईई। मैट्रिकुलेशन होना चाहिए जब आप किसी व्यक्ति से किसी छात्र की तुलना करते हैं तो संख्या को एक बदलाव माना जाना चाहिए? –

उत्तर

0

मैं इस का उपयोग करके इसे कर रहा हूँ:

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

AreEqual सिर्फ Json.Net का उपयोग कर दो वस्तुओं की धारावाहिक संस्करणों की तुलना, यह रहता है:

//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

जब मैं इसका उपयोग करता हूं तो मुझे * पैरामीटर गिनती मिस्चैच * अपवाद मिलता है। –

3

हम 2 सरल तरीके के साथ शुरू संदर्भ प्रकारों और मूल्य प्रकारों का अलग-अलग इलाज करने से।

GetDifference पारित वस्तुओं पर गुणों की जांच करता है और उन्हें व्यक्तिगत रूप से तुलना करता है।

मतभेद की एक सूची प्राप्त करने के लिए:

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

यहाँ आप जो तुम क्या Reflection साथ चाहते हैं करता है एक कोड है।

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

मैंने टाइप किए गए प्रतिनिधियों का उपयोग करके एक तेज़ प्रतिबिंब-आधारित समाधान लिखने की कोशिश करते हुए काफी समय बिताया है। लेकिन अंततः मैंने छोड़ दिया और सामान्य प्रतिबिंब के मुकाबले उच्च प्रदर्शन प्राप्त करने के लिए Marc Gravell'sFast-Member library पर स्विच किया।

कोड:

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

उपयोग:

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

प्रदर्शन:

मेरे बहुत पक्षपाती माप से पता चला है कि इस दृष्टिकोण से के बारे में 4-6 बार तेजी से होता है जेसन-रूपांतरण और लगभग 9 गुना फास सामान्य प्रतिबिंबों की तुलना में ter। लेकिन निष्पक्षता में, आप संभवतः अन्य समाधानों को तेज कर सकते हैं।

सीमाएं:

पल मेरी समाधान नेस्टेड सूचियों से अधिक recurse नहीं करता है, उदाहरण के लिए यह अलग-अलग Subject आइटम की तुलना नहीं करता है पर - यह केवल पता लगाता है कि विषयों सूचियों अलग हैं, यह नहीं कि या कहा पे। हालांकि, जब आपको इसकी आवश्यकता हो तो इस सुविधा को जोड़ने में बहुत मुश्किल नहीं होनी चाहिए। शायद सबसे कठिन हिस्सा यह तय करना होगा कि Difference कक्षा में इन मतभेदों का प्रतिनिधित्व कैसे किया जाए।

1

हर कोई हमेशा फैंसी पाने की कोशिश करता है और डेटा निकालने के इन अत्यधिक सामान्य तरीकों को लिखता है। उस पर एक लागत है।

पुराना स्कूल क्यों आसान नहीं है।

एक GetDifferences सदस्य कार्य व्यक्ति है।

virtual List<String> GetDifferences(Person otherPerson){ 
    var diffs = new List<string>(); 
    if(this.X != otherPerson.X) diffs.add("X"); 
    .... 
} 

विरासत में कक्षाओं में। ओवरराइड करें और अपनी विशिष्ट गुण जोड़ें। बेस फ़ंक्शन जोड़ें।

KISS - इसे सरल रखें। यह आपको लिखने के लिए बंदर के काम के 10 मिनट ले जाएगा, और आप जानते हैं कि यह कुशल और काम करेगा।

+1

यह काम करेगा। लेकिन इसमें 10 मिनट नहीं लगेगा। मेरी कक्षाएं बहुत जटिल हैं जो मैंने दिया उदाहरण। कोड में हंड्रेट लाइनों और उस कोड में संभावित त्रुटियों के साथ इसमें कम से कम एक दिन लग जाएगा। –

+1

सुनिश्चित करें कि आप उस पर perf वजन लें। यदि तुलना की कम संख्या है, तो प्रतिबिंब मार्ग ठीक है। यदि आप इसे सैकड़ों हजारों से अधिक चला रहे हैं तो मैं और अधिक सावधान रहूंगा। – CodeMonkeyForHire

संबंधित मुद्दे