Отрицание 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;
Ответы
Ответ 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);
}
}