比较两个复杂对象的最佳方法

我有两个复杂的对象,如Object1Object2他们有大约5个级别的子对象。

我需要最快的方法来说明他们是否相同。

这怎么可以在C#4.0中完成?

在所有自定义types上实现IEquatable<T> (通常结合inheritance的Object.EqualsObject.GetHashCode方法)。 在复合types的情况下,在包含types中调用包含的types的Equals方法。 对于包含的集合,请使用SequenceEqual扩展方法,该方法内部调用每个元素上的IEquatable<T>.EqualsObject.Equals 。 这种方法显然需要扩展types的定义,但其结果比任何涉及序列化的通用解决scheme都快。

编辑 :这是一个有三层嵌套的人为的例子。

对于值types,通常可以调用它们的Equals方法。 即使字段或属性从未明确分配,它们仍将具有默认值。

对于引用types,您应该首先调用ReferenceEquals ,它会检查引用是否相等 – 当您碰巧引用同一个对象时,这会提高效率。 它也会处理两个引用都为空的情况。 如果检查失败,请确认您的实例的字段或属性不为空(以避免NullReferenceException )并调用其Equals方法。 由于我们的成员是正确的types, IEquatable<T>.Equals方法被直接调用,绕过重写的Object.Equals方法(由于types转换它的执行会稍微慢一些)。

当你重载Object.Equals ,你也应该重写Object.GetHashCode ; 为简明起见,我并没有这样做。

 public class Person : IEquatable<Person> { public int Age { get; set; } public string FirstName { get; set; } public Address Address { get; set; } public override bool Equals(object obj) { return this.Equals(obj as Person); } public bool Equals(Person other) { if (other == null) return false; return this.Age.Equals(other.Age) && ( object.ReferenceEquals(this.FirstName, other.FirstName) || this.FirstName != null && this.FirstName.Equals(other.FirstName) ) && ( object.ReferenceEquals(this.Address, other.Address) || this.Address != null && this.Address.Equals(other.Address) ); } } public class Address : IEquatable<Address> { public int HouseNo { get; set; } public string Street { get; set; } public City City { get; set; } public override bool Equals(object obj) { return this.Equals(obj as Address); } public bool Equals(Address other) { if (other == null) return false; return this.HouseNo.Equals(other.HouseNo) && ( object.ReferenceEquals(this.Street, other.Street) || this.Street != null && this.Street.Equals(other.Street) ) && ( object.ReferenceEquals(this.City, other.City) || this.City != null && this.City.Equals(other.City) ); } } public class City : IEquatable<City> { public string Name { get; set; } public override bool Equals(object obj) { return this.Equals(obj as City); } public bool Equals(City other) { if (other == null) return false; return object.ReferenceEquals(this.Name, other.Name) || this.Name != null && this.Name.Equals(other.Name); } } 

序列化两个对象并比较结果string

如果你不想实现IEquatable,你总是可以使用Reflection来比较所有的属性:如果它们是值types,只是比较它们。 如果它们是引用types,则recursion调用该函数以比较其“内部”属性。

我不是在考虑表演,而是在简单。 这取决于你的对象的确切devise。 它可能会变得复杂,取决于你的物体形状。 然而,有几个解决scheme可以使用,就像这样:

  • 比较.NET对象

我不知道如果以最快的速度,你的意思是最快的方式来实现它或快速运行的代码。 你不应该在知道你是否需要之前进行优化。 不成熟的优化是万恶之源

你可以使用扩展方法recursion来解决这个问题:

 public static bool DeepCompare(this object obj, object another) { if (ReferenceEquals(obj, another)) return true; if ((obj == null) || (another == null)) return false; //Compare two object's class, return false if they are difference if (obj.GetType() != another.GetType()) return false; var result = true; //Get all properties of obj //And compare each other foreach (var property in obj.GetType().GetProperties()) { var objValue = property.GetValue(obj); var anotherValue = property.GetValue(another); if (!objValue.Equals(anotherValue)) result = false; } return result; } public static bool CompareEx(this object obj, object another) { if (ReferenceEquals(obj, another)) return true; if ((obj == null) || (another == null)) return false; if (obj.GetType() != another.GetType()) return false; //properties: int, double, DateTime, etc, not class if (!obj.GetType().IsClass) return obj.Equals(another); var result = true; foreach (var property in obj.GetType().GetProperties()) { var objValue = property.GetValue(obj); var anotherValue = property.GetValue(another); //Recursion if (!objValue.DeepCompare(anotherValue)) result = false; } return result; } 

或者使用Json进行比较(如果对象非常复杂)可以使用Newtonsoft.Json:

 public static bool JsonCompare(this object obj, object another) { if (ReferenceEquals(obj, another)) return true; if ((obj == null) || (another == null)) return false; if (obj.GetType() != another.GetType()) return false; var objJson = JsonConvert.SerializeObject(obj); var anotherJson = JsonConvert.SerializeObject(another); return objJson == anotherJson; } 

序列化这两个对象并通过@JoelFan比较结果string

所以要做到这一点,像这样创build一个静态类,并使用扩展来扩展所有的对象(所以你可以传递任何types的对象,集合等方法)

 using System; using System.IO; using System.Runtime.Serialization.Json; using System.Text; public static class MySerializer { public static string Serialize(this object obj) { var serializer = new DataContractJsonSerializer(obj.GetType()); using (var ms = new MemoryStream()) { serializer.WriteObject(ms, obj); return Encoding.Default.GetString(ms.ToArray()); } } } 

一旦你在任何其他文件中引用这个静态类,你可以这样做:

 Person p = new Person { Firstname = "Jason", LastName = "Argonauts" }; Person p2 = new Person { Firstname = "Jason", LastName = "Argonaut" }; //assuming you have already created a class person! string personString = p.Serialize(); string person2String = p2.Serialize(); 

现在你可以简单地使用.Equals来比较它们。 我用它来检查对象是否也在集合中。 它工作得很好。

我假设你不是指字面上相同的对象

 Object1 == Object2 

你可能会考虑做两者之间的记忆比较

 memcmp(Object1, Object2, sizeof(Object.GetType()) 

但是,这不是真正的代码在C#:)。 因为你所有的数据都可能在堆上创build,所以内存不是连续的,你不能以不可知的方式比较两个对象的相等性。 您将不得不按照自定义的方式逐个比较每个值。

考虑将IEquatable<T>接口添加到您的类,并为您的types定义一个自定义Equals方法。 然后,在那个方法中,手动testing每个值。 如果可以,再次在封闭types上添加IEquatable<T>并重复这个过程。

 class Foo : IEquatable<Foo> { public bool Equals(Foo other) { /* check all the values */ return false; } } 

我发现下面的函数比较对象。

  static bool Compare<T>(T Object1, T object2) { //Get the type of the object Type type = typeof(T); //return false if any of the object is false if (object.Equals(Object1, default(T)) || object.Equals(object2, default(T))) 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; } 

我正在使用它,它对我来说工作正常。

一种方法是覆盖所涉及的每种types的Equals() 。 例如,您的顶级对象将会覆盖Equals()来调用所有5个子对象的Equals()方法。 这些对象也应该都覆盖Equals() ,假设它们是自定义对象,等等,直到通过对顶层对象执行相等性检查来比较整个层次结构。

 public class GetObjectsComparison { public object FirstObject, SecondObject; public BindingFlags BindingFlagsConditions= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; } public struct SetObjectsComparison { public FieldInfo SecondObjectFieldInfo; public dynamic FirstObjectFieldInfoValue, SecondObjectFieldInfoValue; public bool ErrorFound; public GetObjectsComparison GetObjectsComparison; } private static bool ObjectsComparison(GetObjectsComparison GetObjectsComparison) { GetObjectsComparison FunctionGet = GetObjectsComparison; SetObjectsComparison FunctionSet = new SetObjectsComparison(); if (FunctionSet.ErrorFound==false) foreach (FieldInfo FirstObjectFieldInfo in FunctionGet.FirstObject.GetType().GetFields(FunctionGet.BindingFlagsConditions)) { FunctionSet.SecondObjectFieldInfo = FunctionGet.SecondObject.GetType().GetField(FirstObjectFieldInfo.Name, FunctionGet.BindingFlagsConditions); FunctionSet.FirstObjectFieldInfoValue = FirstObjectFieldInfo.GetValue(FunctionGet.FirstObject); FunctionSet.SecondObjectFieldInfoValue = FunctionSet.SecondObjectFieldInfo.GetValue(FunctionGet.SecondObject); if (FirstObjectFieldInfo.FieldType.IsNested) { FunctionSet.GetObjectsComparison = new GetObjectsComparison() { FirstObject = FunctionSet.FirstObjectFieldInfoValue , SecondObject = FunctionSet.SecondObjectFieldInfoValue }; if (!ObjectsComparison(FunctionSet.GetObjectsComparison)) { FunctionSet.ErrorFound = true; break; } } else if (FunctionSet.FirstObjectFieldInfoValue != FunctionSet.SecondObjectFieldInfoValue) { FunctionSet.ErrorFound = true; break; } } return !FunctionSet.ErrorFound; } 

使用具有方法Equals IEquatable<T>接口。

我会这样说:

Object1.Equals(Object2)

将是你在找什么。 那就是如果你想看看对象是否相同,这就是你所要求的。

如果你想检查是否所有的子对象都是相同的,用Equals()方法通过一个循环来运行它们。

非常简单的方法是:

  1. 创build你的对象的列表;
  2. 如果这个列表的交集是“假”,那么它们是相等的。 简单:
  3. 参考:using System.Linq;

     class Program { public class JohnsonObject { public int Id { get; set; } public string Description { get; set; } } static void Main() { JohnsonObject[] obj01 = new[] { new JohnsonObject { Id = 1, Description = "01" } }; JohnsonObject[] obj02 = new[] { new JohnsonObject { Id = 1, Description = "01" } }; Console.Write("Are equal? = {0}", !obj01.Intersect(obj02).Any()); } }