Bucle a través de las propiedades de un objeto en C #

Tengo dos objetos del mismo tipo y quiero recorrer las propiedades públicas de cada uno de ellos y alertar al usuario sobre qué propiedades no coinciden.

¿Es posible hacer esto sin saber qué propiedades contiene el objeto?

Sí, con reflection: suponiendo que cada tipo de propiedad implementa Equals apropiadamente. Una alternativa sería usar ReflectiveEquals recursivamente para todos los tipos conocidos, pero eso se vuelve complicado.

 public bool ReflectiveEquals(object first, object second) { if (first == null && second == null) { return true; } if (first == null || second == null) { return false; } Type firstType = first.GetType(); if (second.GetType() != firstType) { return false; // Or throw an exception } // This will only use public properties. Is that enough? foreach (PropertyInfo propertyInfo in firstType.GetProperties()) { if (propertyInfo.CanRead) { object firstValue = propertyInfo.GetValue(first, null); object secondValue = propertyInfo.GetValue(second, null); if (!object.Equals(firstValue, secondValue)) { return false; } } } return true; } 

Claro que puedes con la reflexión. Aquí está el código para tomar las propiedades de un tipo dado.

 var info = typeof(SomeType).GetProperties(); 

Si puede dar más información sobre lo que está comparando acerca de las propiedades, podemos unir un algoritmo de diferenciación básico. Este código de intstance diferirá en los nombres

 public bool AreDifferent(Type t1, Type t2) { var list1 = t1.GetProperties().OrderBy(x => x.Name).Select(x => x.Name); var list2 = t2.GetProperties().OrderBy(x => x.Name).Select(x => x.Name); return list1.SequenceEqual(list2); } 

Sé que esto es probablemente excesivo, pero aquí está mi clase ObjectComparer que uso para este propósito:

 ///  /// Utility class for comparing objects. ///  public static class ObjectComparer { ///  /// Compares the public properties of any 2 objects and determines if the properties of each /// all contain the same value. ///  /// In cases where object1 and object2 are of different Types (both being derived from Type T) /// we will cast both objects down to the base Type T to ensure the property comparison is only /// completed on COMMON properties. /// (ex. Type T is Foo, object1 is GoodFoo and object2 is BadFoo -- both being inherited from Foo -- /// both objects will be cast to Foo for comparison) ///  ///  /// Any class with public properties. /// Object to compare to object2. /// Object to compare to object1. /// A List of  objects that contain data on the properties /// from object1 that are not equal to the corresponding properties of object2. /// A boolean value indicating whether or not the properties of each object match. public static bool GetDifferentProperties ( T object1 , T object2 , out List propertyInfoList ) where T : class { return GetDifferentProperties( object1 , object2 , null , out propertyInfoList ); } ///  /// Compares the public properties of any 2 objects and determines if the properties of each /// all contain the same value. ///  /// In cases where object1 and object2 are of different Types (both being derived from Type T) /// we will cast both objects down to the base Type T to ensure the property comparison is only /// completed on COMMON properties. /// (ex. Type T is Foo, object1 is GoodFoo and object2 is BadFoo -- both being inherited from Foo -- /// both objects will be cast to Foo for comparison) ///  ///  /// Any class with public properties. /// Object to compare to object2. /// Object to compare to object1. /// A list of  objects /// to ignore when completing the comparison. /// A List of  objects that contain data on the properties /// from object1 that are not equal to the corresponding properties of object2. /// A boolean value indicating whether or not the properties of each object match. public static bool GetDifferentProperties ( T object1 , T object2 , List ignoredProperties , out List propertyInfoList ) where T : class { propertyInfoList = new List(); // If either object is null, we can't compare anything if ( object1 == null || object2 == null ) { return false; } Type object1Type = object1.GetType(); Type object2Type = object2.GetType(); // In cases where object1 and object2 are of different Types (both being derived from Type T) // we will cast both objects down to the base Type T to ensure the property comparison is only // completed on COMMON properties. // (ex. Type T is Foo, object1 is GoodFoo and object2 is BadFoo -- both being inherited from Foo -- // both objects will be cast to Foo for comparison) if ( object1Type != object2Type ) { object1Type = typeof ( T ); object2Type = typeof ( T ); } // Remove any properties to be ignored List comparisonProps = RemoveProperties( object1Type.GetProperties() , ignoredProperties ); foreach ( PropertyInfo object1Prop in comparisonProps ) { Type propertyType = null; object object1PropValue = null; object object2PropValue = null; // Rule out an attempt to check against a property which requires // an index, such as one accessed via this[] if ( object1Prop.GetIndexParameters().GetLength( 0 ) == 0 ) { // Get the value of each property object1PropValue = object1Prop.GetValue( object1 , null ); object2PropValue = object2Type.GetProperty( object1Prop.Name ).GetValue( object2 , null ); // As we are comparing 2 objects of the same type we know // that they both have the same properties, so grab the // first non-null value if ( object1PropValue != null ) propertyType = object1PropValue.GetType().GetInterface( "IComparable" ); if ( propertyType == null ) if ( object2PropValue != null ) propertyType = object2PropValue.GetType().GetInterface( "IComparable" ); } // If both objects have null values or were indexed properties, don't continue if ( propertyType != null ) { // If one property value is null and the other is not null, // they aren't equal; this is done here as a native CompareTo // won't work with a null value as the target if ( object1PropValue == null || object2PropValue == null ) { propertyInfoList.Add( object1Prop ); } else { // Use the native CompareTo method MethodInfo nativeCompare = propertyType.GetMethod( "CompareTo" ); // Sanity Check: // If we don't have a native CompareTo OR both values are null, we can't compare; // hence, we can't confirm the values differ... just go to the next property if ( nativeCompare != null ) { // Return the native CompareTo result bool equal = ( 0 == (int) ( nativeCompare.Invoke( object1PropValue , new object[] {object2PropValue} ) ) ); if ( !equal ) { propertyInfoList.Add( object1Prop ); } } } } } return propertyInfoList.Count == 0; } ///  /// Compares the public properties of any 2 objects and determines if the properties of each /// all contain the same value. ///  /// In cases where object1 and object2 are of different Types (both being derived from Type T) /// we will cast both objects down to the base Type T to ensure the property comparison is only /// completed on COMMON properties. /// (ex. Type T is Foo, object1 is GoodFoo and object2 is BadFoo -- both being inherited from Foo -- /// both objects will be cast to Foo for comparison) ///  ///  /// Any class with public properties. /// Object to compare to object2. /// Object to compare to object1. /// A boolean value indicating whether or not the properties of each object match. public static bool HasSamePropertyValues ( T object1 , T object2 ) where T : class { return HasSamePropertyValues( object1 , object2 , null ); } ///  /// Compares the public properties of any 2 objects and determines if the properties of each /// all contain the same value. ///  /// In cases where object1 and object2 are of different Types (both being derived from Type T) /// we will cast both objects down to the base Type T to ensure the property comparison is only /// completed on COMMON properties. /// (ex. Type T is Foo, object1 is GoodFoo and object2 is BadFoo -- both being inherited from Foo -- /// both objects will be cast to Foo for comparison) ///  ///  /// Any class with public properties. /// Object to compare to object2. /// Object to compare to object1. /// A list of  objects /// to ignore when completing the comparison. /// A boolean value indicating whether or not the properties of each object match. public static bool HasSamePropertyValues ( T object1 , T object2 , List ignoredProperties ) where T : class { // If either object is null, we can't compare anything if ( object1 == null || object2 == null ) { return false; } Type object1Type = object1.GetType(); Type object2Type = object2.GetType(); // In cases where object1 and object2 are of different Types (both being derived from Type T) // we will cast both objects down to the base Type T to ensure the property comparison is only // completed on COMMON properties. // (ex. Type T is Foo, object1 is GoodFoo and object2 is BadFoo -- both being inherited from Foo -- // both objects will be cast to Foo for comparison) if ( object1Type != object2Type ) { object1Type = typeof ( T ); object2Type = typeof ( T ); } // Remove any properties to be ignored List comparisonProps = RemoveProperties( object1Type.GetProperties() , ignoredProperties ); foreach ( PropertyInfo object1Prop in comparisonProps ) { Type propertyType = null; object object1PropValue = null; object object2PropValue = null; // Rule out an attempt to check against a property which requires // an index, such as one accessed via this[] if ( object1Prop.GetIndexParameters().GetLength( 0 ) == 0 ) { // Get the value of each property object1PropValue = object1Prop.GetValue( object1 , null ); object2PropValue = object2Type.GetProperty( object1Prop.Name ).GetValue( object2 , null ); // As we are comparing 2 objects of the same type we know // that they both have the same properties, so grab the // first non-null value if ( object1PropValue != null ) propertyType = object1PropValue.GetType().GetInterface( "IComparable" ); if ( propertyType == null ) if ( object2PropValue != null ) propertyType = object2PropValue.GetType().GetInterface( "IComparable" ); } // If both objects have null values or were indexed properties, don't continue if ( propertyType != null ) { // If one property value is null and the other is not null, // they aren't equal; this is done here as a native CompareTo // won't work with a null value as the target if ( object1PropValue == null || object2PropValue == null ) { return false; } // Use the native CompareTo method MethodInfo nativeCompare = propertyType.GetMethod( "CompareTo" ); // Sanity Check: // If we don't have a native CompareTo OR both values are null, we can't compare; // hence, we can't confirm the values differ... just go to the next property if ( nativeCompare != null ) { // Return the native CompareTo result bool equal = ( 0 == (int) ( nativeCompare.Invoke( object1PropValue , new object[] {object2PropValue} ) ) ); if ( !equal ) { return false; } } } } return true; } ///  /// Removes any  object in the supplied List of /// properties from the supplied Array of properties. ///  /// Array containing master list of ///  objects. /// List of  objects to /// remove from the supplied array of properties. /// A List of  objects. private static List RemoveProperties ( IEnumerable allProperties , IEnumerable propertiesToRemove ) { List innerPropertyList = new List(); // Add all properties to a list for easy manipulation foreach ( PropertyInfo prop in allProperties ) { innerPropertyList.Add( prop ); } // Sanity check if ( propertiesToRemove != null ) { // Iterate through the properties to ignore and remove them from the list of // all properties, if they exist foreach ( PropertyInfo ignoredProp in propertiesToRemove ) { if ( innerPropertyList.Contains( ignoredProp ) ) { innerPropertyList.Remove( ignoredProp ); } } } return innerPropertyList; } } 

El problema real: ¿cómo obtener la diferencia de dos conjuntos?

La forma más rápida que he encontrado es convertir los conjuntos a diccionarios primero, luego diff ’em. Aquí hay un enfoque genérico:

 static IEnumerable DictionaryDiff(Dictionary d1, Dictionary d2) { return from x in d1 where !d2.ContainsKey(x.Key) select x.Value; } 

Entonces puedes hacer algo como esto:

 static public IEnumerable PropertyDiff(Type t1, Type t2) { var d1 = t1.GetProperties().ToDictionary(x => x.Name); var d2 = t2.GetProperties().ToDictionary(x => x.Name); return DictionaryDiff(d1, d2); } 

Comparando dos objetos del mismo tipo usando LINQ y Reflection. ¡NÓTESE BIEN! Esto es básicamente una reescritura de la solución de Jon Skeet, pero con una syntax más compacta y moderna. También debería generar un poco más de efecto IL.

Es algo parecido a esto:

 public bool ReflectiveEquals(LocalHdTicket serverTicket, LocalHdTicket localTicket) { if (serverTicket == null && localTicket == null) return true; if (serverTicket == null || localTicket == null) return false; var firstType = serverTicket.GetType(); // Handle type mismatch anyway you please: if(localTicket.GetType() != firstType) throw new Exception("Trying to compare two different object types!"); return !(from propertyInfo in firstType.GetProperties() where propertyInfo.CanRead let serverValue = propertyInfo.GetValue(serverTicket, null) let localValue = propertyInfo.GetValue(localTicket, null) where !Equals(serverValue, localValue) select serverValue).Any(); } 

Sí. Use la reflexión Con Reflection, puedes hacer cosas como estas:

 //given object of some type object myObjectFromSomewhere; Type myObjOriginalType = myObjectFromSomewhere.GetType(); PropertyInfo[] myProps = myObjOriginalType.GetProperties(); 

Y luego puede usar las clases PropertyInfo resultantes para comparar todo tipo de cosas.

Type.GetProperties listará cada una de las propiedades de un tipo dado. Luego use PropertyInfo.GetValue para verificar los valores.