Могу ли я использовать Linq в Except () с компаратором лямбда-выражений? - PullRequest
29 голосов
/ 08 июня 2011

Я знаю, что могу позвонить в Linq Except и указать собственный IEqualityComparer, но реализация нового класса Comparer для каждого типа данных кажется излишним для этой цели.Могу ли я использовать лямбда-выражение для обеспечения функции равенства, например, когда я использую Where или другие функции LINQ?

Если я не могу, есть ли альтернатива?

Ответы [ 7 ]

41 голосов
/ 02 марта 2012

Для любого, кто еще ищет; Вот еще один способ реализации собственного лямбда-сравнения.

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

затем вы можете создать расширение для linq, кроме пересечения, которое принимает

лямбды
/// <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));
        }

Использование:

var availableItems = allItems.Except(filterItems, (p, p1) => p.Id== p1.Id);
24 голосов
/ 08 июня 2011

Разве вы не можете использовать .Where с лямбдой, которая отфильтровывает ваши необходимые значения?

Пример по запросу:

    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; }
    }
7 голосов
/ 08 июня 2011

Я не думаю, что вы можете напрямую использовать базовые интерфейсы LINQ, но я видел, как люди реализуют класс LambdaComparer с методами расширения, которые помогут вам сделать это.

Вот пример

5 голосов
/ 06 октября 2011

Вот кое-что простое, что я взбил:

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

Использование:

var myItems = allItems.Except(theirItems, new CustomComparer(item => item.Name));
2 голосов
/ 05 сентября 2013

Используйте расширение !

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;

}

Источник

0 голосов
/ 26 октября 2016

Но вы можете попробовать это. Это так просто, я думаю, что код содержит ошибки. Конечно, код для небольших сумм, без LINQ переводится в дБ и т. Д.

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;
}
0 голосов
/ 15 декабря 2015

Здесь '- l, за исключением решения r , основанного на методе LINQ external join :

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

Дает: 1,3

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...