c# linq-to-objects iequalitycomparer

c# - Linq Excepto con IEqualityComparer personalizado



linq-to-objects (3)

La mejor forma de hacerlo es intentar tener una clave única / compuesta en cada objeto y de esa manera usted solo compara la parte clave y no tiene que comparar otros bits.

Podría usar Reflexión, que pasará por cada propiedad pública de la clase (que será muy genérica) y compararlas, pero eso será más lento.

Estoy tratando de encontrar la diferencia entre dos listas genéricas, como en el siguiente ejemplo. Aunque t1 y t2 contienen las mismas propiedades, no son el mismo objeto, por lo que tengo que implementar un IEqualityComparer.

Parece que esto funciona con este ejemplo, pero la clase real tiene varias otras propiedades y también necesito hacer lo mismo con algunas otras clases.

¿Entonces me preguntaba si estoy reinventando la rueda?

¿Existe un método más fácil para comparar todas las propiedades de dos objetos? En este momento, solo necesito hacer frente a las clases que contienen tipos simples, pero sería bueno tener un comparador que funcionara con clases que contienen instancias de otras clases.

void Main() { var t1 = new Sizes { Name = "Test" , Size = 1} ; var t2 = new Sizes { Name = "Test" , Size = 1} ; var list1 = new List<Sizes>(); var list2 = new List<Sizes>(); list1.Add(t1); list2.Add(t2); var differences = list2.Except(list1 , new SizesComparer()); // differences should be empty. } public class Sizes { public string Name { get; set; } public int Size { get; set; } } public class SizesComparer : IEqualityComparer<Sizes> { bool IEqualityComparer<Sizes>.Equals(Sizes x, Sizes y) { return (x.Name.Equals(y.Name) && x.Size.Equals(y.Size)); } int IEqualityComparer<Sizes>.GetHashCode(Sizes obj) { if (Object.ReferenceEquals(obj, null)) return 0; return obj.Name.GetHashCode() + obj.Size; } }


La solución que terminé usando no podía ser descrita como rápida, pero eso no me preocupa y hace lo que quiero, ya que puede reutilizarse y no está restringida a ninguna clase en particular.

Utiliza la biblioteca Newtonsoft.Json para serializar el objeto a una cadena y luego compara el resultado. Esto también tiene la ventaja de trabajar con clases anónimas y clases anidadas.

Supongo que la forma en que funciona la comparación es que primero llama a GetHashCode en ambos objetos y si los coinciden, a continuación, llama a Equals, lo que en esta rutina significará que los objetos coincidentes se serializarán dos veces.

public class JSonEqualityComparer<T> : IEqualityComparer<T> { public bool Equals(T x, T y) { return String.Equals ( Newtonsoft.Json.JsonConvert.SerializeObject(x), Newtonsoft.Json.JsonConvert.SerializeObject(y) ); } public int GetHashCode(T obj) { return Newtonsoft.Json.JsonConvert.SerializeObject(obj).GetHashCode(); } } public static partial class LinqExtensions { public static IEnumerable<T> ExceptUsingJSonCompare<T> (this IEnumerable<T> first, IEnumerable<T> second) { return first.Except(second, new JSonEqualityComparer<T>()); } }

Para usarlo, intercambias Except con ExceptUsingJSonCompare, por ejemplo:

var differences = list2.ExceptUsingJSonCompare(list1);


Podrías probar algo como:

var differences = list2.Where(l2 => !list1.Any(l1 => l1.Name == l2.Name && l1.Size == l2.Size));

O si prefieres:

var differences = list2.Where(l2 => list1.All(l1 => l1.Name != l2.Name || l1.Size != l2.Size));