Петля через объекты в C #
У меня есть два объекта одного типа, и я хочу пропустить общие свойства для каждого из них и предупредить пользователя о том, какие свойства не совпадают.
Возможно ли это сделать, не зная, какие свойства содержит объект?
- Как обнаружить поддержку C ++ 11 компилятора с помощью CMake
- Преобразовать целое число в двоичное в C #
- Как изменить цвет фона кнопки WinAPI C ++
- Как мне обращаться с мьютексами в подвижных типах на C ++?
- segmentation fault с помощью scanf с целым числом
- Как гауссовское размытие изображения без использования встроенных функций gaussian?
- Как проверить статическую функцию
- Использование булевых значений в C
- Каков алгоритм преобразования буквы столбца Excel в его номер?
- Модульное тестирование для Server.MapPath
- Как сделать индексы и верхние индексы с помощью NSAttributedString?
- Сериализовать словарь .NET в JSON Key Value Pair Object
- Почему scanf () вызывает бесконечный цикл в этом коде?
Да, с reflectionм – при условии, что каждый тип свойства реализует Equals
соответствующим образом. Альтернативой будет использование ReflectiveEquals
рекурсивно для всех, кроме некоторых известных типов, но это становится сложным.
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; }
Конечно, вы можете с reflectionм. Вот код для захвата свойств данного типа.
var info = typeof(SomeType).GetProperties();
Если вы можете дать больше информации о том, что вы сравниваете по свойствам, мы можем собрать базовый различный алгоритм n. Этот код для intstance будет отличаться от имен
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); }
Я знаю, что это, вероятно, слишком много, но вот мой class ObjectComparer, который я использую для этой цели:
/// /// 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; } }
Реальная проблема: как получить разницу в двух наборах?
Самый быстрый способ, который я нашел, – сначала преобразовать наборы в словари, а затем разложить их. Вот общий подход:
static IEnumerable DictionaryDiff(Dictionary d1, Dictionary d2) { return from x in d1 where !d2.ContainsKey(x.Key) select x.Value; }
Тогда вы можете сделать что-то вроде этого:
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); }
Сравнение двух объектов одного типа с использованием LINQ и Reflection. NB! Это в основном переписывание решения от Jon Skeet, но с более компактным и современным синтаксисом. Он должен также генерировать немного более эффективный IL.
Это выглядит примерно так:
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(); }
Да. Используйте Reflection . С Reflection вы можете делать такие вещи, как:
//given object of some type object myObjectFromSomewhere; Type myObjOriginalType = myObjectFromSomewhere.GetType(); PropertyInfo[] myProps = myObjOriginalType.GetProperties();
И тогда вы можете использовать полученные classы PropertyInfo для сравнения всех вещей.
Type.GetProperties будет перечислять каждое из свойств данного типа. Затем используйте PropertyInfo.GetValue для проверки значений.