Confronto tra le proprietà degli oggetti in c #

Questo è quello che ho trovato come metodo su una class ereditata da molte delle mie altre classi. L’idea è che permette il semplice confronto tra le proprietà degli oggetti dello stesso tipo.

Ora, questo funziona – ma nell’interesse di migliorare la qualità del mio codice ho pensato di buttarlo fuori per un esame accurato. Come può essere migliore / più efficiente / ecc.?

///  /// Compare property values (as strings) ///  ///  ///  public bool PropertiesEqual(object comparisonObject) { Type sourceType = this.GetType(); Type destinationType = comparisonObject.GetType(); if (sourceType == destinationType) { PropertyInfo[] sourceProperties = sourceType.GetProperties(); foreach (PropertyInfo pi in sourceProperties) { if ((sourceType.GetProperty(pi.Name).GetValue(this, null) == null && destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null) == null)) { // if both are null, don't try to compare (throws exception) } else if (!(sourceType.GetProperty(pi.Name).GetValue(this, null).ToString() == destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null).ToString())) { // only need one property to be different to fail Equals. return false; } } } else { throw new ArgumentException("Comparison object must be of the same type.","comparisonObject"); } return true; } 

Stavo cercando un frammento di codice che avrebbe fatto qualcosa di simile ad aiutare con la scrittura dell’unità test. Ecco cosa ho finito per usare.

 public static bool PublicInstancePropertiesEqual(T self, T to, params string[] ignore) where T : class { if (self != null && to != null) { Type type = typeof(T); List ignoreList = new List(ignore); foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)) { if (!ignoreList.Contains(pi.Name)) { object selfValue = type.GetProperty(pi.Name).GetValue(self, null); object toValue = type.GetProperty(pi.Name).GetValue(to, null); if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))) { return false; } } } return true; } return self == to; } 

MODIFICARE:

Lo stesso codice di cui sopra ma utilizza i metodi LINQ e Estensione:

 public static bool PublicInstancePropertiesEqual(this T self, T to, params string[] ignore) where T : class { if (self != null && to != null) { var type = typeof(T); var ignoreList = new List(ignore); var unequalProperties = from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance) where !ignoreList.Contains(pi.Name) && pi.GetUnderlyingType().IsSimpleType() && pi.GetIndexParameters().Length == 0 let selfValue = type.GetProperty(pi.Name).GetValue(self, null) let toValue = type.GetProperty(pi.Name).GetValue(to, null) where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)) select selfValue; return !unequalProperties.Any(); } return self == to; } public static class TypeExtensions { ///  /// Determine whether a type is simple (String, Decimal, DateTime, etc) /// or complex (ie custom class with public properties and methods). ///  ///  public static bool IsSimpleType( this Type type) { return type.IsValueType || type.IsPrimitive || new[] { typeof(String), typeof(Decimal), typeof(DateTime), typeof(DateTimeOffset), typeof(TimeSpan), typeof(Guid) }.Contains(type) || (Convert.GetTypeCode(type) != TypeCode.Object); } public static Type GetUnderlyingType(this MemberInfo member) { switch (member.MemberType) { case MemberTypes.Event: return ((EventInfo)member).EventHandlerType; case MemberTypes.Field: return ((FieldInfo)member).FieldType; case MemberTypes.Method: return ((MethodInfo)member).ReturnType; case MemberTypes.Property: return ((PropertyInfo)member).PropertyType; default: throw new ArgumentException ( "Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo" ); } } } 

AGGIORNAMENTO: L’ultima versione di Compare-Net-Objects si trova su GitHub , ha pacchetto NuGet e Tutorial . Può essere chiamato come

 //This is the comparison class CompareLogic compareLogic = new CompareLogic(); ComparisonResult result = compareLogic.Compare(person1, person2); //These will be different, write out the differences if (!result.AreEqual) Console.WriteLine(result.DifferencesString); 

O se hai bisogno di cambiare qualche configurazione, usa

 CompareLogic basicComparison = new CompareLogic() { Config = new ComparisonConfig() { MaxDifferences = propertyCount //add other configurations } }; 

L’elenco completo dei parametri configurabili è in ComparisonConfig.cs

Risposta originale:

Le limitazioni che vedo nel tuo codice:

  • Il più grande è che non fa un confronto di oggetti profondi.

  • Non esegue un elemento per confronto di elementi nel caso in cui le proprietà siano elenchi o contengano elenchi come elementi (questo può diventare n livelli).

  • Non tiene conto del fatto che alcuni tipi di proprietà non devono essere confrontati (ad esempio una proprietà Func utilizzata per scopi di filtraggio, come quella nella class PagedCollectionView).

  • Non tiene traccia di quali proprietà erano effettivamente differenti (quindi puoi mostrare le tue asserzioni).

Oggi stavo cercando una soluzione per gli scopi di testing unitario per fare una proprietà di confronto delle proprietà e ho finito per usare: http://comparenetobjects.codeplex.com .

È una libreria gratuita con una sola class che puoi semplicemente usare in questo modo:

 var compareObjects = new CompareObjects() { CompareChildren = true, //this turns deep compare one, otherwise it's shallow CompareFields = false, CompareReadOnly = true, ComparePrivateFields = false, ComparePrivateProperties = false, CompareProperties = true, MaxDifferences = 1, ElementsToIgnore = new List() { "Filter" } }; Assert.IsTrue( compareObjects.Compare(objectA, objectB), compareObjects.DifferencesString ); 

Inoltre, può essere facilmente ricompilato per Silverlight. Basta copiare l’una class in un progetto Silverlight e rimuovere una o due righe di codice per i confronti che non sono disponibili in Silverlight, come il confronto tra membri privati.

Se le prestazioni non contano, puoi serializzarle e confrontare i risultati:

 var serializer = new XmlSerializer(typeof(TheObjectType)); StringWriter serialized1 = new StringWriter(), serialized2 = new StringWriter(); serializer.Serialize(serialized1, obj1); serializer.Serialize(serialized2, obj2); bool areEqual = serialized1.ToString() == serialized2.ToString(); 

Penso che sarebbe meglio seguire lo schema per Override Object # Equals ()
Per una descrizione migliore: leggi Bill Wagner’s Effective C # – Item 9 Penso

 public override Equals(object obOther) { if (null == obOther) return false; if (object.ReferenceEquals(this, obOther) return true; if (this.GetType() != obOther.GetType()) return false; # private method to compare members. return CompareMembers(this, obOther as ThisClass); } 
  • Anche nei metodi che controllano l’uguaglianza, è necessario restituire true o false. o sono uguali o non lo sono .. invece di lanciare un’eccezione, restituire false.
  • Prenderò in considerazione l’override di Object # Equals.
  • Anche se è necessario averlo considerato, l’uso di Reflection per confrontare le proprietà è presumibilmente lento (non ho numeri per eseguire il backup). Questo è il comportamento predefinito per valueType # Equals in C # e si consiglia di sovrascrivere Equals per i tipi di valore e fare un confronto saggio tra membri per il rendimento. (In precedenza ho letto velocemente questo dato che hai una collezione di oggetti Proprietà personalizzati … il mio male.)

Aggiornamento dicembre 2011:

  • Naturalmente, se il tipo ha già una produzione Equals (), allora hai bisogno di un altro approccio.
  • Se stai usando questo per confrontare strutture di dati immutabili esclusivamente per scopi di test, non devi aggiungere un uguale a classi di produzione (qualcuno potrebbe snellire i test chainging l’implementazione Equals o potresti impedire la creazione di un’implementazione Equals richiesta dalla produzione) .

Penso che la risposta di Big T sia stata abbastanza buona ma mancava il confronto approfondito, quindi l’ho ottimizzato un po ‘:

 using System.Collections.Generic; using System.Reflection; /// Comparison class. public static class Compare { /// Compare the public instance properties. Uses deep comparison. /// The reference object. /// The object to compare. /// Ignore property with name. /// Type of objects. /// True if both objects are equal, else false. public static bool PublicInstancePropertiesEqual(T self, T to, params string[] ignore) where T : class { if (self != null && to != null) { var type = self.GetType(); var ignoreList = new List(ignore); foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (ignoreList.Contains(pi.Name)) { continue; } var selfValue = type.GetProperty(pi.Name).GetValue(self, null); var toValue = type.GetProperty(pi.Name).GetValue(to, null); if (pi.PropertyType.IsClass && !pi.PropertyType.Module.ScopeName.Equals("CommonLanguageRuntimeLibrary")) { // Check of "CommonLanguageRuntimeLibrary" is needed because string is also a class if (PublicInstancePropertiesEqual(selfValue, toValue, ignore)) { continue; } return false; } if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))) { return false; } } return true; } return self == to; } } 

Aggiungerei la seguente riga al metodo PublicInstancePropertiesEqual per evitare errori di copia e incolla:

 Assert.AreNotSame(self, to); 

Esegui l’override di .ToString () su tutti gli oggetti che si trovano nelle proprietà? Altrimenti, quel secondo confronto potrebbe tornare con null.

Inoltre, in questo secondo paragone, sono al corrente del costrutto di! (A == B) rispetto a (A! = B), in termini di leggibilità sei mesi / due anni da oggi. La linea stessa è piuttosto ampia, il che è ok se hai un ampio monitor, ma potrebbe non essere stampato molto bene. (Nitpick)

Tutti gli oggetti utilizzano sempre proprietà tali da far funzionare questo codice? Potrebbero esserci alcuni dati interni non corretti che potrebbero essere diversi da un object all’altro, ma tutti i dati esposti sono gli stessi? Sto pensando ad alcuni dati che potrebbero cambiare nel tempo, come due generatori di numeri casuali che raggiungono lo stesso numero in un punto, ma produrranno due diverse sequenze di informazioni, o semplicemente qualsiasi dato che non viene esposto attraverso l’interfaccia di proprietà.

Se si confrontano solo oggetti dello stesso tipo o più in basso nella catena di ereditarietà, perché non specificare il parametro come tipo di base anziché come object?

Effettua anche controlli nulli sul parametro.

Inoltre, farei uso di “var” solo per rendere il codice più leggibile (se il suo codice c # 3)

Inoltre, se l’object ha tipi di riferimento come proprietà, allora stai chiamando ToString () su di essi che in realtà non confronta i valori. Se ToString non viene sovrascritto, restituirà il nome del tipo come stringa che potrebbe restituire falsi positivi.

La prima cosa che suggerirei sarebbe quella di dividere il confronto reale in modo che sia un po ‘più leggibile (ho anche estratto il ToString () – è necessario?):

 else { object originalProperty = sourceType.GetProperty(pi.Name).GetValue(this, null); object comparisonProperty = destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null); if (originalProperty != comparisonProperty) return false; 

Il prossimo suggerimento sarebbe quello di minimizzare l’uso del riflesso il più ansible – è molto lento. Voglio dire, molto lento. Se hai intenzione di farlo, ti suggerirei di mettere in cache i riferimenti di proprietà. Non ho molta familiarità con l’API di Reflection, quindi se questo è un po ‘fuori, basta regolare per renderlo compilabile:

 // elsewhere Dictionary lookupDictionary = new Dictionary; Property[] objectProperties = null; if (lookupDictionary.ContainsKey(sourceType)) { objectProperties = lookupProperties[sourceType]; } else { // build array of Property references PropertyInfo[] sourcePropertyInfos = sourceType.GetProperties(); Property[] sourceProperties = new Property[sourcePropertyInfos.length]; for (int i=0; i < sourcePropertyInfos.length; i++) { sourceProperties[i] = sourceType.GetProperty(pi.Name); } // add to cache objectProperties = sourceProperties; lookupDictionary[object] = sourceProperties; } // loop through and compare against the instances 

Tuttavia, devo dire che sono d'accordo con gli altri poster. Questo ha un odore pigro e inefficiente. Dovresti invece implementare IComparable :-).

qui viene revisionato uno per trattare null = null come uguale

  private bool PublicInstancePropertiesEqual(T self, T to, params string[] ignore) where T : class { if (self != null && to != null) { Type type = typeof(T); List ignoreList = new List(ignore); foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (!ignoreList.Contains(pi.Name)) { object selfValue = type.GetProperty(pi.Name).GetValue(self, null); object toValue = type.GetProperty(pi.Name).GetValue(to, null); if (selfValue != null) { if (!selfValue.Equals(toValue)) return false; } else if (toValue != null) return false; } } return true; } return self == to; } 

Ho finito per fare questo:

  public static string ToStringNullSafe(this object obj) { return obj != null ? obj.ToString() : String.Empty; } public static bool Compare(T a, T b) { int count = a.GetType().GetProperties().Count(); string aa, bb; for (int i = 0; i < count; i++) { aa = a.GetType().GetProperties()[i].GetValue(a, null).ToStringNullSafe(); bb = b.GetType().GetProperties()[i].GetValue(b, null).ToStringNullSafe(); if (aa != bb) { return false; } } return true; } 

Uso:

  if (Compare(a, b)) 

Aggiornare

Se vuoi ignorare alcune proprietà per nome:

  public static string ToStringNullSafe(this object obj) { return obj != null ? obj.ToString() : String.Empty; } public static bool Compare(T a, T b, params string[] ignore) { int count = a.GetType().GetProperties().Count(); string aa, bb; for (int i = 0; i < count; i++) { aa = a.GetType().GetProperties()[i].GetValue(a, null).ToStringNullSafe(); bb = b.GetType().GetProperties()[i].GetValue(b, null).ToStringNullSafe(); if (aa != bb && ignore.Where(x => x == a.GetType().GetProperties()[i].Name).Count() == 0) { return false; } } return true; } 

Uso:

  if (MyFunction.Compare(a, b, "Id","AnotherProp")) 

Puoi ottimizzare il tuo codice chiamando GetProperties solo una volta per tipo:

 public static string ToStringNullSafe(this object obj) { return obj != null ? obj.ToString() : String.Empty; } public static bool Compare(T a, T b, params string[] ignore) { var aProps = a.GetType().GetProperties(); var bProps = b.GetType().GetProperties(); int count = aProps.Count(); string aa, bb; for (int i = 0; i < count; i++) { aa = aProps[i].GetValue(a, null).ToStringNullSafe(); bb = bProps[i].GetValue(b, null).ToStringNullSafe(); if (aa != bb && ignore.Where(x => x == aProps[i].Name).Count() == 0) { return false; } } return true; } 

Per completezza voglio aggiungere un riferimento a http://www.cyotek.com/blog/comparing-the-properties-of-two-objects-via-reflection Ha una logica più completa della maggior parte delle altre risposte in questa pagina.

Comunque preferisco la libreria Compare-Net-Objects https://github.com/GregFinzer/Compare-Net-Objects (riferito dalla risposta di Liviu Trifoi )
La libreria include il pacchetto NuGet http://www.nuget.org/packages/CompareNETObjects e diverse opzioni da configurare.

Assicurati che gli oggetti non siano null .

Avendo obj1 e obj2 :

 if(obj1 == null ) { return false; } return obj1.Equals( obj2 ); 

Funziona anche se gli oggetti sono diversi. potresti personalizzare i metodi nella class delle utilities, magari vuoi confrontare anche le proprietà private …

 using System; using System.Collections.Generic; using System.Linq; using System.Text; class ObjectA { public string PropertyA { get; set; } public string PropertyB { get; set; } public string PropertyC { get; set; } public DateTime PropertyD { get; set; } public string FieldA; public DateTime FieldB; } class ObjectB { public string PropertyA { get; set; } public string PropertyB { get; set; } public string PropertyC { get; set; } public DateTime PropertyD { get; set; } public string FieldA; public DateTime FieldB; } class Program { static void Main(string[] args) { // create two objects with same properties ObjectA a = new ObjectA() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" }; ObjectB b = new ObjectB() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" }; // add fields to those objects a.FieldA = "hello"; b.FieldA = "Something differnt"; if (a.ComparePropertiesTo(b)) { Console.WriteLine("objects have the same properties"); } else { Console.WriteLine("objects have diferent properties!"); } if (a.CompareFieldsTo(b)) { Console.WriteLine("objects have the same Fields"); } else { Console.WriteLine("objects have diferent Fields!"); } Console.Read(); } } public static class Utilities { public static bool ComparePropertiesTo(this Object a, Object b) { System.Reflection.PropertyInfo[] properties = a.GetType().GetProperties(); // get all the properties of object a foreach (var property in properties) { var propertyName = property.Name; var aValue = a.GetType().GetProperty(propertyName).GetValue(a, null); object bValue; try // try to get the same property from object b. maybe that property does // not exist! { bValue = b.GetType().GetProperty(propertyName).GetValue(b, null); } catch { return false; } if (aValue == null && bValue == null) continue; if (aValue == null && bValue != null) return false; if (aValue != null && bValue == null) return false; // if properties do not match return false if (aValue.GetHashCode() != bValue.GetHashCode()) { return false; } } return true; } public static bool CompareFieldsTo(this Object a, Object b) { System.Reflection.FieldInfo[] fields = a.GetType().GetFields(); // get all the properties of object a foreach (var field in fields) { var fieldName = field.Name; var aValue = a.GetType().GetField(fieldName).GetValue(a); object bValue; try // try to get the same property from object b. maybe that property does // not exist! { bValue = b.GetType().GetField(fieldName).GetValue(b); } catch { return false; } if (aValue == null && bValue == null) continue; if (aValue == null && bValue != null) return false; if (aValue != null && bValue == null) return false; // if properties do not match return false if (aValue.GetHashCode() != bValue.GetHashCode()) { return false; } } return true; } } 

Aggiornamento sulla risposta di Liviu sopra – CompareObjects.DifferencesString è stato deprecato.

Funziona bene in un test unitario:

 CompareLogic compareLogic = new CompareLogic(); ComparisonResult result = compareLogic.Compare(object1, object2); Assert.IsTrue(result.AreEqual); 

Questo metodo otterrà le properties della class e confronterà i valori per ogni property . Se uno qualsiasi dei valori è diverso, return false , altrimenti return true .

 public static bool Compare(T Object1, T object2) { //Get the type of the object Type type = typeof(T); //return false if any of the object is false if (Object1 == null || object2 == null) return false; //Loop through each properties inside class and get values for the property from both the objects and compare foreach (System.Reflection.PropertyInfo property in type.GetProperties()) { if (property.Name != "ExtensionData") { string Object1Value = string.Empty; string Object2Value = string.Empty; if (type.GetProperty(property.Name).GetValue(Object1, null) != null) Object1Value = type.GetProperty(property.Name).GetValue(Object1, null).ToString(); if (type.GetProperty(property.Name).GetValue(object2, null) != null) Object2Value = type.GetProperty(property.Name).GetValue(object2, null).ToString(); if (Object1Value.Trim() != Object2Value.Trim()) { return false; } } } return true; } 

Uso:

bool isEqual = Compare(Object1, Object2)

Per espandere la risposta di @nawfal: s, lo uso per testare oggetti di tipi diversi nei miei test di unità per confrontare nomi di proprietà uguali. Nel mio caso quadro di database e DTO.

Usato così nel mio test;

 Assert.IsTrue(resultDto.PublicInstancePropertiesEqual(expectedEntity)); public static bool PublicInstancePropertiesEqual(this T self, Z to, params string[] ignore) where T : class { if (self != null && to != null) { var type = typeof(T); var type2 = typeof(Z); var ignoreList = new List(ignore); var unequalProperties = from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance) where !ignoreList.Contains(pi.Name) let selfValue = type.GetProperty(pi.Name).GetValue(self, null) let toValue = type2.GetProperty(pi.Name).GetValue(to, null) where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)) select selfValue; return !unequalProperties.Any(); } return self == null && to == null; } 

a volte non vuoi confrontare tutte le proprietà pubbliche e vuoi confrontare solo il sottoinsieme di esse, quindi in questo caso puoi semplicemente spostare la logica per confrontare l’elenco di proprietà desiderato con la class astratta

 public abstract class ValueObject where T : ValueObject { protected abstract IEnumerable GetAttributesToIncludeInEqualityCheck(); public override bool Equals(object other) { return Equals(other as T); } public bool Equals(T other) { if (other == null) { return false; } return GetAttributesToIncludeInEqualityCheck() .SequenceEqual(other.GetAttributesToIncludeInEqualityCheck()); } public static bool operator ==(ValueObject left, ValueObject right) { return Equals(left, right); } public static bool operator !=(ValueObject left, ValueObject right) { return !(left == right); } public override int GetHashCode() { int hash = 17; foreach (var obj in this.GetAttributesToIncludeInEqualityCheck()) hash = hash * 31 + (obj == null ? 0 : obj.GetHashCode()); return hash; } } 

e usa questa class astratta in seguito per confrontare gli oggetti

 public class Meters : ValueObject { ... protected decimal DistanceInMeters { get; private set; } ... protected override IEnumerable GetAttributesToIncludeInEqualityCheck() { return new List { DistanceInMeters }; } } 

la mia soluzione si ispira alla risposta di Aras Alenin sopra, in cui ho aggiunto un livello di confronto degli oggetti e un object personalizzato per i risultati del confronto. Sono anche interessato a ottenere il nome della proprietà con il nome dell’object:

  public static IEnumerable GetPublicSimplePropertiesChanged(this T previous, T proposedChange, string[] namesOfPropertiesToBeIgnored) where T : class { return GetPublicGenericPropertiesChanged(previous, proposedChange, namesOfPropertiesToBeIgnored, true, null, null); } public static IReadOnlyList GetPublicGenericPropertiesChanged(this T previous, T proposedChange, string[] namesOfPropertiesToBeIgnored) where T : class { return GetPublicGenericPropertiesChanged(previous, proposedChange, namesOfPropertiesToBeIgnored, false, null, null); } ///  /// Gets the names of the public properties which values differs between first and second objects. /// Considers 'simple' properties AND for complex properties without index, get the simple properties of the children objects. ///  ///  /// The previous object. /// The second object which should be the new one. /// The names of the properties to be ignored. /// if set to true consider simple types only. /// The parent type string. Meant only for recursive call with simpleTypeOnly set to true. /// when calling recursively, the current type of T must be clearly defined here, as T will be more generic (using base class). ///  /// the names of the properties ///  private static IReadOnlyList GetPublicGenericPropertiesChanged(this T previous, T proposedChange, string[] namesOfPropertiesToBeIgnored, bool simpleTypeOnly, string parentTypeString, Type secondType) where T : class { List propertiesChanged = new List(); if (previous != null && proposedChange != null) { var type = secondType == null ? typeof(T) : secondType; string typeStr = parentTypeString + type.Name + "."; var ignoreList = namesOfPropertiesToBeIgnored.CreateList(); IEnumerable> genericPropertiesChanged = from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance) where !ignoreList.Contains(pi.Name) && pi.GetIndexParameters().Length == 0 && (!simpleTypeOnly || simpleTypeOnly && pi.PropertyType.IsSimpleType()) let firstValue = type.GetProperty(pi.Name).GetValue(previous, null) let secondValue = type.GetProperty(pi.Name).GetValue(proposedChange, null) where firstValue != secondValue && (firstValue == null || !firstValue.Equals(secondValue)) let subPropertiesChanged = simpleTypeOnly || pi.PropertyType.IsSimpleType() ? null : GetPublicGenericPropertiesChanged(firstValue, secondValue, namesOfPropertiesToBeIgnored, true, typeStr, pi.PropertyType) let objectPropertiesChanged = subPropertiesChanged != null && subPropertiesChanged.Count() > 0 ? subPropertiesChanged : (new ObjectPropertyChanged(proposedChange.ToString(), typeStr + pi.Name, firstValue.ToStringOrNull(), secondValue.ToStringOrNull())).CreateList() select objectPropertiesChanged; if (genericPropertiesChanged != null) { // get items from sub lists genericPropertiesChanged.ForEach(a => propertiesChanged.AddRange(a)); } } return propertiesChanged; } 

Utilizzare la class seguente per archiviare i risultati del confronto

 [System.Serializable] public class ObjectPropertyChanged { public ObjectPropertyChanged(string objectId, string propertyName, string previousValue, string changedValue) { ObjectId = objectId; PropertyName = propertyName; PreviousValue = previousValue; ProposedChangedValue = changedValue; } public string ObjectId { get; set; } public string PropertyName { get; set; } public string PreviousValue { get; set; } public string ProposedChangedValue { get; set; } } 

E un test unitario di esempio:

  [TestMethod()] public void GetPublicGenericPropertiesChangedTest1() { // Define objects to test Function func1 = new Function { Id = 1, Description = "func1" }; Function func2 = new Function { Id = 2, Description = "func2" }; FunctionAssignment funcAss1 = new FunctionAssignment { Function = func1, Level = 1 }; FunctionAssignment funcAss2 = new FunctionAssignment { Function = func2, Level = 2 }; // Main test: read properties changed var propertiesChanged = Utils.GetPublicGenericPropertiesChanged(funcAss1, funcAss2, null); Assert.IsNotNull(propertiesChanged); Assert.IsTrue(propertiesChanged.Count == 3); Assert.IsTrue(propertiesChanged[0].PropertyName == "FunctionAssignment.Function.Description"); Assert.IsTrue(propertiesChanged[1].PropertyName == "FunctionAssignment.Function.Id"); Assert.IsTrue(propertiesChanged[2].PropertyName == "FunctionAssignment.Level"); }