sintaxis expresiones c# .net linq lambda

c# - expresiones - linq expression



¿Puedo especificar mi comparador de tipo explícito en línea? (6)

Así que .NET 3.0 / 3.5 nos proporciona muchas formas nuevas de consultar, ordenar y manipular datos, gracias a todas las funciones ordenadas proporcionadas con LINQ. A veces, necesito comparar los tipos definidos por el usuario que no tienen un operador de comparación incorporado. En muchos casos, la comparación es realmente simple, algo así como foo1.key? = Foo2.key. En lugar de crear un nuevo IEqualityComparer para el tipo, ¿puedo simplemente especificar la comparación en línea utilizando delegaciones anónimas / funciones lambda? Algo como:

var f1 = ..., f2 = ...; var f3 = f1.Except( f2, new IEqualityComparer( (Foo a, Foo b) => a.key.CompareTo(b.key) ) );

Estoy bastante seguro de que lo anterior en realidad no funciona. Simplemente no quiero tener que hacer algo tan "pesado" como una clase entera solo para decirle al programa cómo comparar manzanas con manzanas.


¿Por qué no algo como esto?

public class Comparer<T> : IEqualityComparer<T> { private readonly Func<T, T, bool> _equalityComparer; public Comparer(Func<T, T, bool> equalityComparer) { _equalityComparer = equalityComparer; } public bool Equals(T first, T second) { return _equalityComparer(first, second); } public int GetHashCode(T value) { return value.GetHashCode(); } }

y luego podría hacer, por ejemplo, algo así como (por ejemplo, en el caso de Intersect en IEnumerable<T> ):

list.Intersect(otherList, new Comparer<T>( (x, y) => x.Property == y.Property));

La clase Comparer puede colocarse en un proyecto de utilidades y usarse donde sea que se necesite.

Solo ahora veo la respuesta de Sam Saffron (que es muy similar a esta).



Me parece que proporcionar ayuda adicional en IEnumerable es una forma más limpia de hacerlo.

Ver: esta pregunta

Entonces podrías tener:

var f3 = f1.Except( f2, (a, b) => a.key.CompareTo(b.key) );

Si define los métodos de extensión adecuadamente


Mi biblioteca MiscUtil contiene un ProjectionComparer para construir un IComparer <T> de un delegado de proyección. Sería el trabajo de 10 minutos hacer un ProjectionEqualityComparer para hacer lo mismo.

EDITAR: Aquí está el código para ProjectionEqualityComparer:

using System; using System.Collections.Generic; /// <summary> /// Non-generic class to produce instances of the generic class, /// optionally using type inference. /// </summary> public static class ProjectionEqualityComparer { /// <summary> /// Creates an instance of ProjectionEqualityComparer using the specified projection. /// </summary> /// <typeparam name="TSource">Type parameter for the elements to be compared</typeparam> /// <typeparam name="TKey">Type parameter for the keys to be compared, /// after being projected from the elements</typeparam> /// <param name="projection">Projection to use when determining the key of an element</param> /// <returns>A comparer which will compare elements by projecting /// each element to its key, and comparing keys</returns> public static ProjectionEqualityComparer<TSource, TKey> Create<TSource, TKey>(Func<TSource, TKey> projection) { return new ProjectionEqualityComparer<TSource, TKey>(projection); } /// <summary> /// Creates an instance of ProjectionEqualityComparer using the specified projection. /// The ignored parameter is solely present to aid type inference. /// </summary> /// <typeparam name="TSource">Type parameter for the elements to be compared</typeparam> /// <typeparam name="TKey">Type parameter for the keys to be compared, /// after being projected from the elements</typeparam> /// <param name="ignored">Value is ignored - type may be used by type inference</param> /// <param name="projection">Projection to use when determining the key of an element</param> /// <returns>A comparer which will compare elements by projecting /// each element to its key, and comparing keys</returns> public static ProjectionEqualityComparer<TSource, TKey> Create<TSource, TKey> (TSource ignored, Func<TSource, TKey> projection) { return new ProjectionEqualityComparer<TSource, TKey>(projection); } } /// <summary> /// Class generic in the source only to produce instances of the /// doubly generic class, optionally using type inference. /// </summary> public static class ProjectionEqualityComparer<TSource> { /// <summary> /// Creates an instance of ProjectionEqualityComparer using the specified projection. /// </summary> /// <typeparam name="TKey">Type parameter for the keys to be compared, /// after being projected from the elements</typeparam> /// <param name="projection">Projection to use when determining the key of an element</param> /// <returns>A comparer which will compare elements by projecting each element to its key, /// and comparing keys</returns> public static ProjectionEqualityComparer<TSource, TKey> Create<TKey>(Func<TSource, TKey> projection) { return new ProjectionEqualityComparer<TSource, TKey>(projection); } } /// <summary> /// Comparer which projects each element of the comparison to a key, and then compares /// those keys using the specified (or default) comparer for the key type. /// </summary> /// <typeparam name="TSource">Type of elements which this comparer /// will be asked to compare</typeparam> /// <typeparam name="TKey">Type of the key projected /// from the element</typeparam> public class ProjectionEqualityComparer<TSource, TKey> : IEqualityComparer<TSource> { readonly Func<TSource, TKey> projection; readonly IEqualityComparer<TKey> comparer; /// <summary> /// Creates a new instance using the specified projection, which must not be null. /// The default comparer for the projected type is used. /// </summary> /// <param name="projection">Projection to use during comparisons</param> public ProjectionEqualityComparer(Func<TSource, TKey> projection) : this(projection, null) { } /// <summary> /// Creates a new instance using the specified projection, which must not be null. /// </summary> /// <param name="projection">Projection to use during comparisons</param> /// <param name="comparer">The comparer to use on the keys. May be null, in /// which case the default comparer will be used.</param> public ProjectionEqualityComparer(Func<TSource, TKey> projection, IEqualityComparer<TKey> comparer) { if (projection == null) { throw new ArgumentNullException("projection"); } this.comparer = comparer ?? EqualityComparer<TKey>.Default; this.projection = projection; } /// <summary> /// Compares the two specified values for equality by applying the projection /// to each value and then using the equality comparer on the resulting keys. Null /// references are never passed to the projection. /// </summary> public bool Equals(TSource x, TSource y) { if (x == null && y == null) { return true; } if (x == null || y == null) { return false; } return comparer.Equals(projection(x), projection(y)); } /// <summary> /// Produces a hash code for the given value by projecting it and /// then asking the equality comparer to find the hash code of /// the resulting key. /// </summary> public int GetHashCode(TSource obj) { if (obj == null) { throw new ArgumentNullException("obj"); } return comparer.GetHashCode(projection(obj)); } }

Y aquí hay un uso de muestra:

var f3 = f1.Except(f2, ProjectionEqualityComparer<Foo>.Create(a => a.key));


Para juegos pequeños, puede hacer:

f3 = f1.Where(x1 => f2.All(x2 => x2.key != x1.key));

Para juegos grandes, deseará algo más eficiente en la búsqueda como:

var tmp = new HashSet<string>(f2.Select(f => f.key)); f3 = f1.Where(f => tmp.Add(f.key));

Pero, aquí, el Type de clave debe implementar IEqualityComparer (arriba supuse que era una string ). Por lo tanto, esto realmente no responde su pregunta sobre el uso de una lambda en esta situación, pero sí utiliza menos código que algunas de las respuestas que sí lo hacen.

Puede confiar en el optimizador y acortar la segunda solución a:

f3 = f1.Where(x1 => (new HashSet<string>(f2.Select(x2 => x2.key))).Add(x1.key));

pero no hice pruebas para saber si funciona a la misma velocidad. Y ese trazador de líneas de uno puede ser demasiado listo para mantener.


aquí hay una clase de ayuda simple que debe hacer lo que quieras

public class EqualityComparer<T> : IEqualityComparer<T> { public EqualityComparer(Func<T, T, bool> cmp) { this.cmp = cmp; } public bool Equals(T x, T y) { return cmp(x, y); } public int GetHashCode(T obj) { return obj.GetHashCode(); } public Func<T, T, bool> cmp { get; set; } }

puedes usarlo así:

processed.Union(suburbs, new EqualityComparer<Suburb>((s1, s2) => s1.SuburbId == s2.SuburbId));