Подтвердить что ты не робот

Отрицание Func <T, bool> в выражении лямбда

Func<T, bool> expr = x => x.Prop != 1;

somelist = somelist.Where(expr);

Пока все хорошо. Но я хотел бы отрицать expr следующим образом:

somelist = somelist.Where(!expr);

В результате возникает ошибка компиляции: Cannot apply ! operator to operand of type Func<T, bool>.

Должен ли я создать для него другую переменную выражения?

Func<T, bool> expr2 = x => x.Prop == 1;
4b9b3361

Ответ 1

Func<T, bool> expr = x => x.Prop != 1;

Func<T, bool> negativeExpr = value => !expr(value);

или же

somelist = somelist.Where(value => !expr(value));

При использовании деревьев выражений следующее будет работать:

Expression<Func<T, bool>> expr = x => x.Prop != 1;

var negativeExpr = Expression.Lambda<Func<T, bool>>(
    Expression.Not(expr.Body), 
    expr.Parameters);

somelist = somelist.Where(negativeExpr);

Чтобы сделать вашу жизнь проще, вы можете создать следующие методы расширения:

public static Func<T, bool> Not<T>(
    this Func<T, bool> predicate)
{
    return value => !predicate(value);
}

public static Expression<Func<T, bool>> Not<T>(
    this Expression<Func<T, bool>> expr)
{
    return Expression.Lambda<Func<T, bool>>(
        Expression.Not(expr.Body), 
        expr.Parameters);
}

Теперь вы можете сделать это:

somelist = somelist.Where(expr.Not());

Ответ 2

Я просто собираюсь бросить это там как глупый ответ. Чтобы быть ясным: я бы этого не сделал, и я не рекомендую, чтобы кто-то это делал.:)

Мне хотелось посмотреть, можно ли получить синтаксис somelist.Where(!expr) или что-то в этом роде.

Ну, мне это удалось, и я ненавижу себя.

var expr = N.egatable<MyClass>(x => x.Prop != 1);
somelist = someList.Where(!expr);

N.egatable был просто небольшим помощником синтаксиса для удобства и в значительной степени ненужным (EDIT: я хотел, чтобы явным образом не определял MyClass или каким-то образом скрывал создание оболочки объекта, но не мог туда добраться, подумал, может быть, у кого-то была бы лучшая идея):

public static class N
{
    public static Negator<T> egatable<T>(Func<T, bool> underlyingFunction)
    {
        return new Negator<T>(underlyingFunction);
    }
}

Negator<T> есть реальная "магия":

public class Negator<T>
{
    private Func<T, bool> UnderlyingFunction;

    public Negator(Func<T, bool> underlyingFunction)
    {
        this.UnderlyingFunction = underlyingFunction;
    }

    public static implicit operator Func<T, bool>(Negator<T> neg)
    {
        return v => neg.UnderlyingFunction(v);
    }

    public static Negator<T> operator !(Negator<T> neg)
    {
        return new Negator<T>(v => !neg.UnderlyingFunction(v));
    }
}

Сначала перегрузка оператора ! выполняет отрицание функции (как в этом ответе), то оператор неявного преобразования в Func<T, bool> позволяет ему перейти к метод расширения Where.

Возможно, очень глупо, вы можете продолжать перебрасывать его вперед и назад следующим образом:

somelist = someList.Where(!!expr);
somelist = someList.Where(!!!expr);
somelist = someList.Where(!!!!expr);
somelist = someList.Where(!!!!!expr);
somelist = someList.Where(!!!!!!expr); //oh my what

Итак, снова... пожалуйста, не делайте этого.:) Определенно придерживайтесь правильного/разумного способа делать вещи, как в ответе Стивена.

EDIT: здесь реализация с использованием выражений, которая работает точно так же с точки зрения использования синтаксиса. Не уверен, что он "правильный" и не протестировал его против Entity Framework:

public class ExpressionNegator<T>
{
    private Expression<Func<T, bool>> UnderlyingExpression;

    public ExpressionNegator(Expression<Func<T, bool>> underlyingExpression)
    {
        this.UnderlyingExpression = underlyingExpression;
    }

    public static implicit operator Func<T, bool>(ExpressionNegator<T> neg)
    {
        return neg.UnderlyingExpression.Compile();
    }

    public static implicit operator Expression<Func<T, bool>>(ExpressionNegator<T> neg)
    {
        return neg.UnderlyingExpression;
    }

    public static ExpressionNegator<T> operator !(ExpressionNegator<T> neg)
    {
        var originalExpression = neg.UnderlyingExpression;
        Expression<Func<T, bool>> negatedExpression = originalExpression.Update(
            Expression.Not(originalExpression.Body), 
            originalExpression.Parameters);
        return new ExpressionNegator<T>(negatedExpression);
    }
}