30
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;
4

2 回答 2

59
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());
于 2013-08-29T13:03:29.510 回答
18

我只是把这个作为一个愚蠢的答案扔出去。明确一点:我不会这样做,也不建议任何人这样做。:)

我有点想看看是否有可能获得somelist.Where(!expr)语法或类似的东西。

好吧,我成功了,我讨厌自己。

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

N.egatable只是一个小的便利语法助手,并且在很大程度上是不必要的(编辑:我避免必须显式定义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

所以再次......请不要这样做。:) 绝对坚持正确/理智的做事方式,如史蒂文的回答。

编辑:这是一个使用表达式的实现,它在语法使用方面的工作方式完全相同。不确定它是否“正确”,并且没有针对实体框架进行测试:

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);
    }
}
于 2013-08-29T13:31:39.807 回答