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 se podría hacer esto en C # 4.0?
Implemente IEquatable<T>
(generalmente junto con la sustitución de los métodos heredados Object.Equals
y Object.GetHashCode
) en todos sus tipos personalizados. En el caso de tipos compuestos, invoque el Equals
método de los tipos contenidos dentro de los tipos contenedores. Para colecciones contenidas, use el SequenceEqual
método de extensión, que llama internamente IEquatable<T>.Equals
o Object.Equals
en cada elemento. Obviamente, este enfoque requerirá que extienda 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 anidamiento.
Para los tipos de valor, normalmente puede simplemente llamar a su Equals
método. Incluso si los campos o las propiedades nunca se asignaron explícitamente, seguirían teniendo un valor predeterminado.
Para los tipos de referencia, primero debe llamar ReferenceEquals
, lo que verifica la igualdad de referencia; esto serviría como un impulso de eficiencia cuando se hace referencia al mismo objeto. También manejaría casos en los que ambas referencias sean nulas. Si esa verificación falla, confirme que el campo o la propiedad de su instancia no sea nulo (para evitar NullReferenceException
) y llame a su Equals
método. Dado que nuestros miembros están escritos correctamente, el IEquatable<T>.Equals
método se llama directamente, sin pasar por el Object.Equals
método 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<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);
}
}
Actualización : esta respuesta fue escrita hace varios años. Desde entonces, comencé a alejarme de la implementación IEquality<T>
de tipos mutables para tales escenarios. Hay dos nociones de igualdad: identidad y equivalencia . A nivel de representación de la memoria, estos se distinguen popularmente como "igualdad de referencia" e "igualdad de valor" (consulte Comparaciones de igualdad ). Sin embargo, la misma distinción también se puede aplicar a nivel de dominio. Suponga que su Person
clase tiene una PersonId
propiedad, única por persona distinta del mundo real. Si dos objetos con los mismos PersonId
, pero diferentes Age
valores consideradas iguales o diferentes? La respuesta anterior supone que se busca la equivalencia. Sin embargo, hay muchos usos delIEquality<T>
interfaz, como colecciones, que asumen que tales implementaciones proporcionan identidad . Por ejemplo, si está rellenando un HashSet<T>
, normalmente esperaría que una TryGetValue(T,T)
llamada devuelva elementos existentes que comparten simplemente la identidad de su argumento, no necesariamente elementos equivalentes cuyos contenidos son completamente iguales. Esta noción es reforzada por las notas sobre GetHashCode
:
En general, para tipos de referencia mutables, debe anular
GetHashCode()
solo si:
- Puede calcular el código hash a partir de campos que no son mutables; o
- Puede asegurarse de que el código hash de un objeto mutable no cambie mientras el objeto esté contenido en una colección que se base en su código hash.
partial
, en cuyo caso, sí, puede implementar suEquals
método a través de una declaración de clase parcial agregada manualmente que hace referencia a campos / propiedades de la generación automática uno.Enumerable.SequenceEqual
método de las matrices:this.Addresses.SequenceEqual(other.Addresses)
. Esto llamaría internamente a suAddress.Equals
método para cada par de direcciones correspondientes, dado que laAddress
clase implementa laIEquatable<Address>
interfaz.Serialice ambos objetos y compare las cadenas resultantes
fuente
+1
así simplemente porque nunca pensé en hacer una comparación de igualdad basada en valores de esta manera. Es bonito y sencillo. Sería bueno ver algunos puntos de referencia con este código.Puede utilizar el método de extensión, recursividad para resolver este problema:
o compare usando Json (si el objeto es muy complejo) Puede usar Newtonsoft.Json:
fuente
DeepCompare
lugar de simplemente llamar de formaCompareEx
recursiva?result
conreturn false
lo haría más eficiente.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 de forma recursiva para comparar sus propiedades "internas" .
No estoy pensando en el rendimiento, sino en la sencillez. Depende, sin embargo, del diseño exacto de sus objetos. Podría complicarse dependiendo de la forma de sus objetos (por ejemplo, si hay dependencias cíclicas entre propiedades). Sin embargo, existen varias soluciones que puede utilizar, como esta:
Otra opción es serializar el objeto como texto, por ejemplo, usando JSON.NET y comparando el resultado de la serialización. (JSON.NET puede manejar dependencias cíclicas entre propiedades).
No sé si por más rápido te refieres a la forma más rápida de implementarlo o un código que se ejecuta rápido. No debe optimizar antes de saber si es necesario. La optimización temprana es la raíz de todo mal
fuente
IEquatable<T>
implementación califique como un caso de optimización prematura. La reflexión será drásticamente más lenta. La implementación predeterminada deEquals
para tipos de valores personalizados utiliza la reflexión; Microsoft mismo recomienda anularlo por rendimiento: "Anule elEquals
método para un tipo en particular para mejorar el rendimiento del método y representar más de cerca el concepto de igualdad para el tipo".Serialice ambos objetos y compare las cadenas resultantes por @JoelFan
Entonces, para hacer esto, cree una clase estática como esta y use Extensions para extender TODOS los objetos (para que pueda pasar cualquier tipo de objeto, colección, etc. al método)
Una vez que haga referencia a esta clase estática en cualquier otro archivo, puede hacer esto:
Ahora simplemente puede usar .Equals para compararlos. Utilizo esto para verificar si los objetos también están en colecciones. Funciona muy bien.
fuente
CultrureInfo
. Esto solo funcionaría si los datos internos son principalmente cadenas y números enteros. De lo contrario, sería un desastre.Asumiré que no te refieres literalmente a los mismos objetos.
Quizás esté pensando en hacer una comparación de memoria entre los dos
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 puede simplemente comparar la igualdad de dos objetos de manera agnóstica. Tendrá que comparar cada valor, uno a la vez, de forma personalizada.
Considere agregar la
IEquatable<T>
interfaz a su clase y defina unEquals
método personalizado para su tipo. Luego, en ese método, pruebe manualmente cada valor. AgregueIEquatable<T>
nuevamente los tipos adjuntos si puede y repita el proceso.fuente
Serialice ambos objetos, luego calcule el código hash y luego compare.
fuente
Encontré esta función a continuación para comparar objetos.
Lo estoy usando y me está funcionando bien.
fuente
if
significa aquelloCompare(null, null) == false
que no es lo que esperaría.Basado en algunas respuestas ya dadas aquí, decidí respaldar principalmente la respuesta de JoelFan . Me encantan los métodos de extensión y estos han funcionado muy bien para mí cuando ninguna de las otras soluciones los usaba para comparar mis clases complejas.
Métodos de extensión
Ejemplos de uso
fuente
Yo diría que:
Object1.Equals(Object2)
sería lo que estás buscando. Eso es si está buscando para ver si los objetos son iguales, que es lo que parece estar preguntando.
Si desea verificar si todos los objetos secundarios son iguales, ejecútelos a través de un ciclo con el
Equals()
método.fuente
fuente
Una forma de hacer esto sería anular
Equals()
en cada tipo involucrado. Por ejemplo, su objeto de nivel superior anularíaEquals()
para llamar alEquals()
método de los 5 objetos secundarios. Todos esos objetos también deben anularseEquals()
, asumiendo que son objetos personalizados, y así sucesivamente hasta que se pueda comparar toda la jerarquía con solo realizar una verificación de igualdad en los objetos de nivel superior.fuente
Utilice la
IEquatable<T>
interfaz que tiene un métodoEquals
.fuente
Gracias al ejemplo de Jonathan. Lo expandí para todos los casos (matrices, listas, diccionarios, tipos primitivos).
Esta es una comparación sin serialización y no requiere la implementación de ninguna interfaz para los objetos comparados.
Para copiar fácilmente el repositorio creado con código
fuente
Ahora puede usar json.net. Simplemente vaya a Nuget e instálelo.
Y puedes hacer algo como esto:
Quizás podría crear un método de extensión para el objeto si quisiera ser más elegante. Tenga en cuenta que esto solo compara las propiedades públicas. Y si desea ignorar una propiedad pública cuando realiza la comparación, puede usar el atributo [JsonIgnore].
fuente