Есть ли способ отрицать предикат? - PullRequest
24 голосов
/ 30 января 2010

Я хочу сделать что-то вроде этого:

List<SomeClass> list1 = ...
List<SomeClass> list2 = ...
Predicate<SomeClass> condition = ...

...

list2.RemoveAll (!condition);

...

list2.AddRange (list1.FindAll (condition));

Однако это приводит к ошибке компилятора, так как ! не может быть применено к Predicate<SomeClass>.Есть ли способ сделать это?

Ответы [ 2 ]

32 голосов
/ 30 января 2010

Вы можете использовать лямбда-выражение для определения на месте анонимного делегата, который является результатом отрицания результата предиката:

list.RemoveAll(x => !condition(x));    

Другой вариант:

static Predicate<T> Negate<T>(Predicate<T> predicate) {
     return x => !predicate(x);
}

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

// list is List<T> some T
// predicate is Predicate<T> some T
list.RemoveAll(Negate(predicate));

Причина, по которой list.RemoveAll(!condition) не работает, заключается в том, что для делегатов не определен оператор !. Вот почему вы должны определить новый делегат в терминах condition, как показано выше.

9 голосов
/ 30 января 2010

Это на самом деле возможно, но, возможно, в несколько иной форме, чем вы привыкли. В .NET лямбда-выражения можно интерпретировать как делегаты ИЛИ как деревья выражений . Относительно просто выполнить операцию NOT над деревом выражений.

Вот пример использования вашего кода в качестве отправной точки:

namespace Sample
{
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;

    internal class ExpressionSample
    {
        private static Expression<TDelegate> Negate<TDelegate>(Expression<TDelegate> expression)
        {
            return Expression.Lambda<TDelegate>(Expression.Not(expression.Body), expression.Parameters);
        }

        private static void Main()
        {
            // Match any string of length 2 or more characters
            Expression<Predicate<string>> expression = (s) => s.Length > 1;

            // Logical negation, i.e. match string of length 1 or fewer characters
            Expression<Predicate<string>> negatedExpression = ExpressionSample.Negate(expression);

            // Compile expressions to predicates
            Predicate<string> predicate = expression.Compile();
            Predicate<string> negativePredicate = negatedExpression.Compile();

            List<string> list1 = new List<string> { string.Empty, "an item", "x", "another item" };
            List<string> list2 = new List<string> { "yet another item", "still another item", "y", string.Empty };

            list2.RemoveAll(negativePredicate);
            list2.AddRange(list1.FindAll(predicate));

            list2.ForEach((s) => Console.WriteLine(s));
        }
    }
}
...