C#中两个对象之间的差异

我想知道如何找到同一类的两个对象之间的区别。 因此,如果我有一个Person类,唯一的区别是Age,它将返回不同的字段/字段。

谢谢

这不是C#(或.NET真正)直接支持的东西,但是您可以手动为特定类型实现某些东西,或者编写使用reflection来区分任意对象的代码。

如果选择后者,则必须确定要进入对象图的深度,以确定两个实例是否相同,以及如何比较某些基本类型的相等性(例如,双精度)。

编写基于reflection的差分算法比起初看起来更困难 – 个人而言,我会直接为您需要它的类型(或帮助类)实现此function。

这是我在调试时使用的一些简单代码:

//This structure represents the comparison of one member of an object to the corresponding member of another object. public struct MemberComparison { public readonly MemberInfo Member; //Which member this Comparison compares public readonly object Value1, Value2;//The values of each object's respective member public MemberComparison(MemberInfo member, object value1, object value2) { Member = member; Value1 = value1; Value2 = value2; } public override string ToString() { return Member.Name + ": " + Value1.ToString() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToString(); } } //This method can be used to get a list of MemberComparison values that represent the fields and/or properties that differ between the two objects. public List ReflectiveCompare(T x, T y) { List list = new List();//The list to be returned foreach (MemberInfo m in typeof(T).GetMembers(BindingFlags.NonPublic | BindingFlags.Instance)) //Only look at fields and properties. //This could be changed to include methods, but you'd have to get values to pass to the methods you want to compare if (m.MemberType == MemberTypes.Field) { FieldInfo field = (FieldInfo)m; var xValue = field.GetValue(x); var yValue = field.GetValue(y); if (!object.Equals(xValue, yValue))//Add a new comparison to the list if the value of the member defined on 'x' isn't equal to the value of the member defined on 'y'. list.Add(new MemberComparison(field, yValue, xValue)); } else if (m.MemberType == MemberTypes.Property) { var prop = (PropertyInfo)m; if (prop.CanRead && prop.GetGetMethod().GetParameters().Length == 0) { var xValue = prop.GetValue(x, null); var yValue = prop.GetValue(y, null); if (!object.Equals(xValue, yValue)) list.Add(new MemberComparison(prop, xValue, yValue)); } else//Ignore properties that aren't readable or are indexers continue; } return list; } 

要使用它,您的代码可能如下所示:

 public static void Main() { MyObject object1 = new MyObject(); MyObject object2 = new MyObject(); // ...Code that changes object1 and/or object2... //Here's your answer: a list of what's different between the 2 objects, and each of their different values. //No type parameters are needed here- typeof(MyObject) is implied by the coincident types of both parameters. List changes = ReflectiveCompare(object1, object2); } 

这实际上取决于你想要比较实体的深度,但这里的反思是最好的。 代码将是这样的:

 public class Pair { public object Value1 { get; set; } public object Value2 { get; set; } } //somewhere in another class public Dictionary Compare(T object1, T object2) { var props = typeof(T).GetProperties().Where(pi => pi.CanRead); //this will return only public readable properties. Modify if you need something different Dictionary result = new Dictionary(); foreach (var prop in props) { var val1 = prop.GetValue(object1, null); //indexing properties are ignored here var val2 = prop.GetValue(object2, null); if (val1 != val2) //maybe some more sophisticated compare algorithm here, using IComparable, nested objects analysis etc. { result[prop.Name] = new Pair { Value1 = val1, Value2 = val2 }; } } return result; } 

如果您需要深度处理嵌套对象,那么,如前所述,您将需要一些哈希表来记住已处理的对象并防止它们再次被处理。 希望这可以帮助!

我使用了Michael Hoffmann的答案,但是如果其中一个属性为null,并且如果一个抛出错误(通常在比较“Type”对象时发现),或者如果一个是集合,我发现它缺乏支持。

虽然仍有工作要做,但我在这里发布了基本修改代码:

  public struct MemberComparison { public readonly System.Reflection.MemberInfo Member; //Which member this Comparison compares public readonly object Value1, Value2;//The values of each object's respective member public readonly Exception Value1Exception, Value2Exception; public MemberComparison(System.Reflection.MemberInfo member, object value1, object value2, Exception value1Exception = null, Exception value2Exception = null) { Member = member; Value1 = value1; Value2 = value2; Value1Exception = value1Exception; Value2Exception = value2Exception; } public override string ToString() { if (Value1Exception != null && Value2Exception != null) { if (Value1Exception.GetType().Equals(Value2Exception.GetType())) { return Member.Name + ": Exception in both, same exception type of type "+Value1Exception.GetType().Name+", message in first exception: " +Value1Exception.Message+", message in second exception: "+Value2Exception.Message+", differences in type value: " + string.Join("\n", ReflectiveCompare(Value1Exception, Value2Exception).ToArray()); } else if (!Value1Exception.GetType().Equals(Value2Exception.GetType())) { return Member.Name + ": Exception in both, different exception type: " + Value1Exception.GetType().Name + " : " + Value2Exception.GetType().Name+", message in first exception: " +Value1Exception.Message+", message in second exception: "+Value2Exception.Message; } } else if (Value1Exception != null && Value2Exception == null) { return Member.Name + ": "+ Value2.ToString()+" Exception in first of type " + Value1Exception.GetType().Name+", message is: "+Value1Exception.Message; } else if (Value1Exception == null && Value2Exception != null) { return Member.Name + ": "+ Value1.ToString()+" Exception in second of type " + Value2Exception.GetType().Name+", message is: "+Value2Exception.Message; } return Member.Name + ": " + Value1.ToString() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToString(); } } public static bool isCollection(object obj) { return obj.GetType().GetInterfaces() .Any(iface => (iface.GetType() == typeof(ICollection) || iface.GetType() == typeof(IEnumerable) || iface.GetType() == typeof(IList)) || (iface.IsGenericTypeDefinition && (iface.GetGenericTypeDefinition() == typeof(ICollection<>) || iface.GetGenericTypeDefinition() == typeof(IEnumerable<>) || iface.GetGenericTypeDefinition() == typeof(IList<>)))); } //This method can be used to get a list of MemberComparison values that represent the fields and/or properties that differ between the two objects. public static List ReflectiveCompare(T x, T y) { List list = new List();//The list to be returned var memb = typeof(T).GetMembers(); foreach (System.Reflection.MemberInfo m in memb) //Only look at fields and properties. //This could be changed to include methods, but you'd have to get values to pass to the methods you want to compare if (m.MemberType == System.Reflection.MemberTypes.Field) { System.Reflection.FieldInfo field = (System.Reflection.FieldInfo)m; Exception excep1 = null; Exception excep2 = null; object xValue = null; object yValue = null; try { xValue = field.GetValue(x); } catch (Exception e) { excep1 = e; } try { yValue = field.GetValue(y); } catch (Exception e) { excep2 = e; } if ((excep1 != null && excep2 == null) || (excep1 == null && excep2 != null)) { list.Add(new MemberComparison(field, yValue, xValue, excep1, excep2)); } else if (excep1 != null && excep2 != null && !excep1.GetType().Equals(excep2.GetType())) { list.Add(new MemberComparison(field, yValue, xValue, excep1, excep2)); } else if (excep1 != null && excep2 != null && excep1.GetType().Equals(excep2.GetType()) && ReflectiveCompare(excep1, excep2).Count > 0) { list.Add(new MemberComparison(field, yValue, xValue, excep1, excep2)); } else if ((xValue == null && yValue == null)) { continue; } else if (xValue == null || yValue == null) list.Add(new MemberComparison(field, yValue, xValue)); else if (!xValue.Equals(yValue) && ((!isCollection(xValue) && !isCollection(yValue)) || (isCollection(xValue) && !isCollection(yValue)) || (!isCollection(xValue) && isCollection(yValue)) || (isCollection(xValue) && isCollection(yValue) && ReflectiveCompare(xValue, yValue).Count > 0)))//Add a new comparison to the list if the value of the member defined on 'x' isn't equal to the value of the member defined on 'y'. list.Add(new MemberComparison(field, yValue, xValue)); } else if (m.MemberType == System.Reflection.MemberTypes.Property) { var prop = (System.Reflection.PropertyInfo)m; if (prop.CanRead && !(prop.GetGetMethod() == null || prop.GetGetMethod().GetParameters() == null) && prop.GetGetMethod().GetParameters().Length == 0) { Exception excep1 = null; Exception excep2 = null; object xValue = null; object yValue = null; try { xValue = prop.GetValue(x, null); } catch (Exception e) { excep1 = e; } try { yValue = prop.GetValue(y, null); } catch (Exception e) { excep2 = e; } if ((excep1 != null && excep2 == null) || (excep1 == null && excep2 != null)) { list.Add(new MemberComparison(prop, yValue, xValue, excep1, excep2)); } else if (excep1 != null && excep2 != null && !excep1.GetType().Equals(excep2.GetType())) { list.Add(new MemberComparison(prop, yValue, xValue, excep1, excep2)); } else if (excep1 != null && excep2 != null && excep1.GetType().Equals(excep2.GetType()) && ReflectiveCompare(excep1, excep2).Count > 0) { list.Add(new MemberComparison(prop, yValue, xValue, excep1, excep2)); } else if ((xValue == null && yValue == null)) { continue; } else if (xValue == null || yValue == null) list.Add(new MemberComparison(prop, yValue, xValue)); else if (!xValue.Equals(yValue) && ((!isCollection(xValue) && !isCollection(yValue)) || (isCollection(xValue) && !isCollection(yValue)) || (!isCollection(xValue) && isCollection(yValue)) || (isCollection(xValue) && isCollection(yValue) && ReflectiveCompare(xValue,yValue).Count > 0)))// || (isCollection(xValue) && isCollection(yValue) && ((IEnumerable)xValue).OrderBy(i => i).SequenceEqual(xValue.OrderBy(i => i))) ))) list.Add(new MemberComparison(prop, xValue, yValue)); } else//Ignore properties that aren't readable or are indexers continue; } return list; } 

这样的事情怎么样?

这将为您提供两个对象之间不同的属性名称列表。 我不认为这一直是您正在寻找的解决方案,但我认为这是一个不错的开始

 Foo foo1 = new Foo { Prop1 = "One", Prop2 = "Two"}; Foo foo2 = new Foo { Prop1 = "One", Prop2 = "Three" }; Type fooType = typeof (Foo); PropertyInfo[] properties = fooType.GetProperties(); var diffs = from property in properties let first = foo1 let second = foo2 where property.GetValue(first, null) != property.GetValue(second, null) select property; 

在我的示例中,这将返回“Prop2”,因为这是属性,其值在对象之间不同。

编辑 :当然,这假设您的对象中的任何复杂类型实现了您所期望的相等比较。 如果不是,您需要潜入对象图并按照其他人的建议进行嵌套比较

您需要递归遍历整个对象图上的所有私有和公共属性和字段。 使用HashSet跟踪已经检查过的对象,这样就不会返回重复的结果或进入堆栈溢出。

如果属性类型是IComparable,则可以将该属性的值强制转换为IComparable并使用IComparable.CompareTo。 如果没有,则必须以递归方式调用子对象上的差分方法。