tutorial expresiones ejemplos c# .net linq

expresiones - linq c#



¿Puedo usar Linq''s Except() con un comparador de expresiones lambda? (7)

Sé que puedo llamar a Excepto de linq y especificar un IEqualityComparer personalizado, pero la implementación de una nueva clase de Comparador para cada tipo de datos parece una exageración para este propósito. ¿Puedo usar una expresión lambda para proporcionar la función de igualdad, como cuando utilizo ¿Dónde u otras funciones de LINQ?

Si no puedo, ¿hay alguna alternativa?


¿No puede usar un .Where con un lambda que filtra sus valores requeridos?

Ejemplo solicitado:

static void Main(string[] args) { var firstCustomers = new[] { new Customer { Id = 1, Name = "Bob" }, new Customer { Id = 2, Name = "Steve" } }; var secondCustomers = new[] { new Customer { Id = 2, Name = "Steve" }, new Customer { Id = 3, Name = "John" } }; var customers = secondCustomers.Where(c => !firstCustomers.Select(fc => fc.Id).Contains(c.Id)); } public class Customer { public int Id { get; set; } public string Name { get; set; } }


Aquí está la solución r , basada en una técnica de unión externa LINQ :

from l in new[] { 1, 2, 3 } join r in new[] { 2, 4, 5 } on l equals r into rr where !rr.Any() select l

Rendimiento: 1, 3.


Aquí hay algo simple que me dio una paliza:

public class CustomComparer<TSource, TCompareType> : IEqualityComparer<TSource> where TSource : class { private readonly Func<TSource, TCompareType> getComparisonObject; public CustomComparer(Func<TSource,TCompareType> getComparisonObject) { if (getComparisonObject == null) throw new ArgumentNullException("getComparisonObject"); this.getComparisonObject = getComparisonObject; } /// <summary> /// Determines whether the specified objects are equal. /// </summary> /// <returns> /// true if the specified objects are equal; otherwise, false. /// </returns> /// <param name="x">The first object of type <paramref name="T"/> to compare. /// </param><param name="y">The second object of type <paramref name="T"/> to compare. /// </param> public bool Equals(TSource x, TSource y) { if (x == null) { return (y == null); } else if (y == null) { return false; } return EqualityComparer<TCompareType>.Default.Equals(getComparisonObject(x), getComparisonObject(y)); } /// <summary> /// Returns a hash code for the specified object. /// </summary> /// <returns> /// A hash code for the specified object. /// </returns> /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned. /// </param><exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null. /// </exception> public int GetHashCode(TSource obj) { return EqualityComparer<TCompareType>.Default.GetHashCode(getComparisonObject(obj)); } }

Uso:

var myItems = allItems.Except(theirItems, new CustomComparer(item => item.Name));


No creo que puedas directamente con las interfaces LINQ básicas, pero he visto a personas implementar una clase de LambdaComparer con métodos de extensión que te ayudarán a hacerlo.

Aquí un ejemplo


Para cualquiera que siga buscando; Aquí hay otra forma de implementar un comparador lambda personalizado.

public class LambdaComparer<T> : IEqualityComparer<T> { private readonly Func<T, T, bool> _expression; public LambdaComparer(Func<T, T, bool> lambda) { _expression = lambda; } public bool Equals(T x, T y) { return _expression(x, y); } public int GetHashCode(T obj) { /* If you just return 0 for the hash the Equals comparer will kick in. The underlying evaluation checks the hash and then short circuits the evaluation if it is false. Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects), you will always fall through to the Equals check which is what we are always going for. */ return 0; } }

luego puede crear una extensión para el linq Excepto un Intersect que toma lambda

/// <summary> /// Returns all items in the first collection except the ones in the second collection that match the lambda condition /// </summary> /// <typeparam name="T">The type</typeparam> /// <param name="listA">The first list</param> /// <param name="listB">The second list</param> /// <param name="lambda">The filter expression</param> /// <returns>The filtered list</returns> public static IEnumerable<T> Except<T>(this IEnumerable<T> listA, IEnumerable<T> listB, Func<T, T, bool> lambda) { return listA.Except(listB, new LambdaComparer<T>(lambda)); } /// <summary> /// Returns all items in the first collection that intersect the ones in the second collection that match the lambda condition /// </summary> /// <typeparam name="T">The type</typeparam> /// <param name="listA">The first list</param> /// <param name="listB">The second list</param> /// <param name="lambda">The filter expression</param> /// <returns>The filtered list</returns> public static IEnumerable<T> Intersect<T>(this IEnumerable<T> listA, IEnumerable<T> listB, Func<T, T, bool> lambda) { return listA.Intersect(listB, new LambdaComparer<T>(lambda)); }

Uso:

var availableItems = allItems.Except(filterItems, (p, p1) => p.Id== p1.Id);


Pero puedes intentar esto. Esto es muy fácil, creo que el código contiene errores. Por supuesto, el código es para pequeñas cantidades, sin LINQ se traduce a db, etc.

public static IEnumerable<TSource> ExceptPredicate<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TSource, bool> compare) { foreach (var itmFirst in first) { if (!second.Any(itmsecond => compare(itmFirst, itmsecond))) { yield return itmFirst; } } yield break; }


Usa una extension !

public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKey) { return from item in items join otherItem in other on getKey(item) equals getKey(otherItem) into tempItems from temp in tempItems.DefaultIfEmpty() where ReferenceEquals(null, temp) || temp.Equals(default(T)) select item; }

Source