La mejor forma de comparar dos objetos complejos

Tengo dos objetos complejos como Object1 y Object2 . Tienen alrededor de 5 niveles de objetos secundarios.

Necesito el método más rápido para decir si son iguales o no.

¿Cómo podría hacerse esto en C # 4.0?

Implemente IEquatable (generalmente junto con la anulación de los Object.Equals heredados Object.Equals y Object.GetHashCode ) en todos sus tipos personalizados. En el caso de los tipos compuestos, invoque los tipos contenidos ‘Método Equals dentro de los tipos que lo contienen. Para colecciones contenidas, use el método de extensión SequenceEqual , que internamente llama a IEquatable.Equals o Object.Equals en cada elemento. Este enfoque obviamente requerirá que amplíe las definiciones de sus tipos, pero sus resultados son más rápidos que cualquier solución genérica que involucre la serialización.

Editar : Aquí hay un ejemplo artificial con tres niveles de anidación.

Para tipos de valor, normalmente puede simplemente llamar a su método Equals . Incluso si los campos o las propiedades nunca se asignaron explícitamente, aún tendrían un valor predeterminado.

Para los tipos de referencia, primero debe llamar a ReferenceEquals , que verifica la igualdad de referencia; esto serviría como un refuerzo de la eficiencia cuando se refiera al mismo objeto. También manejaría casos donde ambas referencias son nulas. Si esa comprobación falla, confirme que el campo o la propiedad de su instancia no es nulo (para evitar NullReferenceException ) y llame a su método Equals . Como nuestros miembros están correctamente tipados, se llama directamente al método IEquatable.Equals , eludiendo el método Object.Equals anulado (cuya ejecución sería un poco más lenta debido al tipo de conversión).

Cuando anula Object.Equals , también se espera que anule Object.GetHashCode ; No lo hice a continuación en aras de la concisión.

 public class Person : IEquatable { 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
{ 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 { 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); } }

Serializar ambos objetos y comparar las cadenas resultantes

Puede usar el método de extensión, recursión para resolver este problema:

 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; } 

o compare usando Json (si el objeto es muy complejo) Puede usar 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; } 

Si no desea implementar IEquatable, siempre puede usar Reflection para comparar todas las propiedades: si son de tipo de valor, simplemente compárelas. Si son de tipo de referencia, llame a la función recursivamente para comparar sus propiedades “internas”.

No estoy pensando en el rendimiento, sino en la simplicidad. Depende, sin embargo, del diseño exacto de tus objetos. Podría ser complicado dependiendo de la forma de su objeto. Sin embargo, hay varias soluciones que puedes usar, como esta:

  • Comparar objetos .NET

No sé si por el más rápido te refieres a la manera más rápida de implementarlo o un código que corre rápido. No debe optimizar antes de saber si es necesario. La optimización temprana es la raíz de todo mal

Serializar ambos objetos y comparar las cadenas resultantes por @JoelFan

Para hacer esto, crea una clase estática como esa y usa Extensiones para extender TODOS los objetos (para que puedas pasar cualquier tipo de objeto, colección, etc. al método)

 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()); } } } 

Una vez que haga referencia a esta clase estática en cualquier otro archivo, puede hacer esto:

 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(); 

Ahora puede simplemente usar .Equals para compararlos. Yo uso esto para verificar si los objetos están en colecciones también. Funciona realmente bien.

Asumo que no te estás refiriendo literalmente a los mismos objetos

 Object1 == Object2 

Usted podría estar pensando en hacer una comparación de memoria entre los dos

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

Pero eso ni siquiera es código real en c # :). Debido a que todos sus datos probablemente se crean en el montón, la memoria no es contigua y no se puede simplemente comparar la igualdad de dos objetos de manera agnóstica. Tendrás que comparar cada valor, uno cada vez, de forma personalizada.

Considere añadir la IEquatable a su clase, y defina un método Equals personalizado para su tipo. Luego, en ese método, prueba manualmente cada valor. Agregue IEquatable nuevamente en los tipos incluidos si puede y repita el proceso.

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

Encontré esta función a continuación para comparar objetos.

  static bool Compare(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; } 

Lo estoy usando y está funcionando bien para mí.

 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; } 

Una forma de hacer esto sería anular Equals() en cada tipo involucrado. Por ejemplo, su objeto de nivel superior anularía Equals() para llamar al método Equals() de los 5 objetos secundarios. Esos objetos también deberían anular Equals() , suponiendo que son objetos personalizados, y así sucesivamente hasta que toda la jerarquía se pueda comparar simplemente realizando una comprobación de igualdad en los objetos de nivel superior.

Use la IEquatable que tiene un método Equals .

Yo diría que:

Object1.Equals(Object2)

sería lo que estás buscando. Eso es si estás buscando ver si los objetos son iguales, que es lo que pareces estar preguntando.

Si desea verificar si todos los objetos secundarios son iguales, ejecútelos mediante un ciclo con el método Equals() .