유형이 같은 두 개의 객체가 있으며 각 객체의 공용 속성을 반복하고 일치하지 않는 속성에 대해 사용자에게 경고하고 싶습니다.
객체에 어떤 속성이 포함되어 있는지 모른 채이 작업을 수행 할 수 있습니까?
답변
그렇습니다 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;
}
답변
물론 당신은 반사와 함께 할 수 있습니다. 주어진 유형에서 속성을 가져 오는 코드는 다음과 같습니다.
var info = typeof(SomeType).GetProperties();
당신이 속성에 대해 비교하고있는 것에 대해 더 많은 정보를 제공 할 수 있다면 우리는 기본 diffing 알고리즘을 만들 수 있습니다. 이 코드는 이름에 따라 다릅니다.
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);
}
답변
나는 이것이 과잉 일 것임을 알고 있지만,이 목적을 위해 사용하는 ObjectComparer 클래스는 다음과 같습니다.
/// <summary>
/// Utility class for comparing objects.
/// </summary>
public static class ObjectComparer
{
/// <summary>
/// Compares the public properties of any 2 objects and determines if the properties of each
/// all contain the same value.
/// <para>
/// 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)
/// </para>
/// </summary>
/// <typeparam name="T">Any class with public properties.</typeparam>
/// <param name="object1">Object to compare to object2.</param>
/// <param name="object2">Object to compare to object1.</param>
/// <param name="propertyInfoList">A List of <see cref="PropertyInfo"/> objects that contain data on the properties
/// from object1 that are not equal to the corresponding properties of object2.</param>
/// <returns>A boolean value indicating whether or not the properties of each object match.</returns>
public static bool GetDifferentProperties<T> ( T object1 , T object2 , out List<PropertyInfo> propertyInfoList )
where T : class
{
return GetDifferentProperties<T>( object1 , object2 , null , out propertyInfoList );
}
/// <summary>
/// Compares the public properties of any 2 objects and determines if the properties of each
/// all contain the same value.
/// <para>
/// 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)
/// </para>
/// </summary>
/// <typeparam name="T">Any class with public properties.</typeparam>
/// <param name="object1">Object to compare to object2.</param>
/// <param name="object2">Object to compare to object1.</param>
/// <param name="ignoredProperties">A list of <see cref="PropertyInfo"/> objects
/// to ignore when completing the comparison.</param>
/// <param name="propertyInfoList">A List of <see cref="PropertyInfo"/> objects that contain data on the properties
/// from object1 that are not equal to the corresponding properties of object2.</param>
/// <returns>A boolean value indicating whether or not the properties of each object match.</returns>
public static bool GetDifferentProperties<T> ( T object1 , T object2 , List<PropertyInfo> ignoredProperties , out List<PropertyInfo> propertyInfoList )
where T : class
{
propertyInfoList = new List<PropertyInfo>();
// 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<PropertyInfo> 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;
}
/// <summary>
/// Compares the public properties of any 2 objects and determines if the properties of each
/// all contain the same value.
/// <para>
/// 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)
/// </para>
/// </summary>
/// <typeparam name="T">Any class with public properties.</typeparam>
/// <param name="object1">Object to compare to object2.</param>
/// <param name="object2">Object to compare to object1.</param>
/// <returns>A boolean value indicating whether or not the properties of each object match.</returns>
public static bool HasSamePropertyValues<T> ( T object1 , T object2 )
where T : class
{
return HasSamePropertyValues<T>( object1 , object2 , null );
}
/// <summary>
/// Compares the public properties of any 2 objects and determines if the properties of each
/// all contain the same value.
/// <para>
/// 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)
/// </para>
/// </summary>
/// <typeparam name="T">Any class with public properties.</typeparam>
/// <param name="object1">Object to compare to object2.</param>
/// <param name="object2">Object to compare to object1.</param>
/// <param name="ignoredProperties">A list of <see cref="PropertyInfo"/> objects
/// to ignore when completing the comparison.</param>
/// <returns>A boolean value indicating whether or not the properties of each object match.</returns>
public static bool HasSamePropertyValues<T> ( T object1 , T object2 , List<PropertyInfo> 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<PropertyInfo> 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;
}
/// <summary>
/// Removes any <see cref="PropertyInfo"/> object in the supplied List of
/// properties from the supplied Array of properties.
/// </summary>
/// <param name="allProperties">Array containing master list of
/// <see cref="PropertyInfo"/> objects.</param>
/// <param name="propertiesToRemove">List of <see cref="PropertyInfo"/> objects to
/// remove from the supplied array of properties.</param>
/// <returns>A List of <see cref="PropertyInfo"/> objects.</returns>
private static List<PropertyInfo> RemoveProperties (
IEnumerable<PropertyInfo> allProperties , IEnumerable<PropertyInfo> propertiesToRemove )
{
List<PropertyInfo> innerPropertyList = new List<PropertyInfo>();
// 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;
}
}
답변
실제 문제 : 두 세트의 차이점을 얻는 방법?
내가 찾은 가장 빠른 방법은 먼저 집합을 사전으로 변환 한 다음 diff ’em입니다. 일반적인 접근 방식은 다음과 같습니다.
static IEnumerable<T> DictionaryDiff<K, T>(Dictionary<K, T> d1, Dictionary<K, T> d2)
{
return from x in d1 where !d2.ContainsKey(x.Key) select x.Value;
}
그런 다음 다음과 같이 할 수 있습니다.
static public IEnumerable<PropertyInfo> 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);
}
답변
예. 반사를 사용하십시오 . Reflection을 사용하면 다음과 같은 작업을 수행 할 수 있습니다.
//given object of some type
object myObjectFromSomewhere;
Type myObjOriginalType = myObjectFromSomewhere.GetType();
PropertyInfo[] myProps = myObjOriginalType.GetProperties();
그런 다음 결과 PropertyInfo 클래스를 사용하여 모든 방식을 비교할 수 있습니다.
답변
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();
}