Comparando propriedades do object em c #

Isto é o que eu tenho como método em uma class herdada por muitas das minhas outras classs. A ideia é que permite a simples comparação entre propriedades de objects do mesmo tipo.

Agora, isso funciona – mas, no interesse de melhorar a qualidade do meu código, pensei em descartá-lo para exame minucioso. Como pode ser melhor / mais eficiente / etc?

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

    Eu estava procurando por um trecho de código que faria algo semelhante para ajudar a escrever o teste de unidade. Aqui está o que acabei usando.

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

    EDITAR:

    O mesmo código acima, mas usa os methods LINQ e Extension:

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

    ATUALIZAÇÃO: A versão mais recente do Compare-Net-Objects está localizada no GitHub , possui o pacote NuGet e o Tutorial . Pode ser chamado como

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

    Ou se você precisar alterar alguma configuração, use

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

    A lista completa de parâmetros configuráveis ​​está em ComparisonConfig.cs

    Resposta original:

    As limitações que vejo no seu código:

    • O maior deles é que não faz uma comparação profunda de objects.

    • Ele não faz um elemento por comparação de elemento, caso as propriedades sejam listas ou contenham listas como elementos (isso pode ser n-níveis).

    • Não leva em conta que algum tipo de propriedades não deve ser comparado (por exemplo, uma propriedade Func usada para fins de filtragem, como a da class PagedCollectionView).

    • Ele não rastreia quais propriedades realmente eram diferentes (para que você possa mostrar em suas asserções).

    Eu estava procurando por alguma solução para fins de teste de unidade para fazer propriedade por comparação profunda de propriedade e acabei usando: http://comparenetobjects.codeplex.com .

    É uma biblioteca livre com apenas uma class que você pode simplesmente usar assim:

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

    Além disso, pode ser facilmente recompilado para o Silverlight. Basta copiar a class em um projeto do Silverlight e remover uma ou duas linhas de código para comparações que não estão disponíveis no Silverlight, como comparação de membros privados.

    Se o desempenho não importa, você poderia serializá-los e comparar os resultados:

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

    Eu acho que seria melhor seguir o padrão de Override Object # Equals ()
    Para uma melhor descrição: Leia C # efetivo de Bill Wagner – Item 9 eu acho

     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); } 
    • Também em methods que verificam a igualdade, você deve retornar true ou false. ou eles são iguais ou não são. Em vez de lançar uma exceção, retorne false.
    • Eu consideraria replace o Object # Equals.
    • Mesmo que você tenha considerado isso, usar o Reflection para comparar propriedades é supostamente lento (não tenho números para fazer isso). Esse é o comportamento padrão para valueType # Equals em C # e é recomendável que você substitua Equals para os tipos de valor e faça uma comparação inteligente entre os membros para obter desempenho. (Anteriormente eu li isso rapidamente, já que você tem uma coleção de objects Property personalizados … é ruim.)

    Atualização de dezembro de 2011:

    • Naturalmente, se o tipo já tiver um Equals de produção (), você precisará de outra abordagem.
    • Se você estiver usando isso para comparar estruturas de dados imutáveis ​​exclusivamente para fins de teste, não deverá adicionar Equals a classs de produção (Alguém pode manipular os testes encaminhando a implementação de Equals ou você pode impedir a criação de uma implementação de Equals necessária à produção) .

    Eu acho que a resposta do Big T foi muito boa, mas a comparação profunda estava faltando, então eu ajustei um pouco:

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

    Eu adicionaria a seguinte linha ao método PublicInstancePropertiesEqual para evitar erros de copiar e colar:

     Assert.AreNotSame(self, to); 

    Você substitui .ToString () em todos os seus objects que estão nas propriedades? Caso contrário, essa segunda comparação poderia retornar com null.

    Além disso, nessa segunda comparação, estou em cima do constructo de! (A == B) em comparação com (A! = B), em termos de legibilidade seis meses / dois anos a partir de agora. A linha em si é bem ampla, o que é bom se você tiver um monitor grande, mas não imprimir muito bem. (nitpick)

    Todos os seus objects estão sempre usando propriedades para que esse código funcione? Poderia haver alguns dados internos não proprietários que poderiam ser diferentes de um object para outro, mas todos os dados expostos são os mesmos? Estou pensando em alguns dados que podem mudar com o tempo, como dois geradores de números randoms que atingem o mesmo número em um ponto, mas vão produzir duas seqüências diferentes de informações, ou apenas quaisquer dados que não sejam expostos. através da interface da propriedade.

    Se você estiver apenas comparando objects do mesmo tipo ou mais abaixo na cadeia de inheritance, por que não especificar o parâmetro como seu tipo base, em vez de object?

    Também faça verificações nulas no parâmetro também.

    Além disso, eu faria uso de ‘var’ apenas para tornar o código mais legível (se seu código c # 3)

    Além disso, se o object tiver tipos de referência como propriedades, você estará chamando ToString (), o que realmente não compara valores. Se ToString não for sobrescrito, ele retornará o nome do tipo como uma string que poderia retornar falsos positivos.

    A primeira coisa que eu gostaria de sugerir seria dividir a comparação real para que seja um pouco mais legível (eu também tirei o ToString () – é necessário?):

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

    A próxima sugestão seria minimizar o máximo possível o uso da reflection – é muito lento. Quero dizer, muito devagar. Se você for fazer isso, sugiro colocar em cache as referências de propriedade. Eu não estou intimamente familiarizado com a Reflection API, então se isso é um pouco fora, basta ajustar para torná-lo compilar:

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

    No entanto, devo dizer que concordo com os outros cartazes. Isso cheira preguiçoso e ineficiente. Você deve estar implementando IComparable :-).

    aqui é revisado um para tratar null = null como igual

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

    Eu acabei fazendo isso:

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

    Atualizar

    Se você quiser ignorar algumas propriedades por 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")) 

    Você pode otimizar seu código chamando GetProperties somente uma vez por 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; } 

    Para completar, quero adicionar referência a http://www.cyotek.com/blog/comparing-the-properties-of-two-objects-via-reflection. Ela tem uma lógica mais completa do que a maioria das outras respostas nesta página.

    No entanto, prefiro a biblioteca Compare-Net-Objects https://github.com/GregFinzer/Compare-Net-Objects (referida pela resposta de Liviu Trifoi )
    A biblioteca tem o pacote NuGet http://www.nuget.org/packages/CompareNETObjects e várias opções para configurar.

    Certifique-se de que os objects não sejam null .

    Tendo obj1 e obj1 :

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

    Isso funciona mesmo se os objects forem diferentes. você poderia personalizar os methods na class de utilitários, talvez você queira comparar propriedades privadas também …

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

    Atualização sobre a resposta de Liviu acima – CompareObjects.DifferencesString foi preterido.

    Isso funciona bem em um teste de unidade:

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

    Este método irá obter properties da class e comparar os valores para cada property . Se algum dos valores for diferente, ele return false , senão ele 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)

    Para expandir a resposta do @nawfal: s, eu uso isso para testar objects de tipos diferentes em meus testes de unidade para comparar nomes de propriedades iguais. No meu caso, entidade de database e DTO.

    Usado assim no meu teste;

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

    às vezes você não quer comparar todas as propriedades públicas e deseja comparar apenas o subconjunto delas, então, neste caso, você pode apenas mover a lógica para comparar a lista desejada de propriedades à class abstrata.

     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 use essa class abstrata mais tarde para comparar os objects

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

    minha solução inspirou-se na resposta de Aras Alenin acima, onde adicionei um nível de comparação de objects e um object personalizado para resultados de comparação. Também estou interessado em obter o nome da propriedade com o nome do 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; } 

    Usando a seguinte class para armazenar resultados de comparação

     [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 um teste de unidade de amostra:

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