Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Negating Func<T, bool> in lambda expression [duplicate]

Tags:

c#

asp.net

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

somelist = somelist.Where(expr);

So far so good. But I would like to negate expr like this:

somelist = somelist.Where(!expr);

Which result in a compile error: Cannot apply ! operator to operand of type Func<T, bool>.

Do I have to create another expression variable for this?

Func<T, bool> expr2 = x => x.Prop == 1;
like image 322
Johan Avatar asked Aug 29 '13 13:08

Johan


2 Answers

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

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

or

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

When using expression trees the following will do the trick:

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

To make your life easier, you can create the following extension methods:

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

Now you can do this:

somelist = somelist.Where(expr.Not());
like image 52
Steven Avatar answered Oct 24 '22 09:10

Steven


I'm just going to throw this out there as a silly answer. Just to be clear: I would not do this, and I do not recommend that anyone does this. :)

I kind of wanted to see if it was possible to get the somelist.Where(!expr) syntax or something like it.

Well I succeeded, and I hate myself.

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

The N.egatable was just a small convenience syntax helper and largely unnecessary (EDIT: I wanted to avoid having to explicitly define MyClass or somehow make the instantiation of the object wrapper hidden, but couldn't quite get there and thought maybe someone would have a better idea):

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

Negator<T> is where the real "magic" happens:

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

First the ! operator overload performs the function negation (just as in this answer), then the implicit conversion operator to Func<T, bool> lets it pass in to the Where extension method.

Perhaps very silly is you can keep flipping it back and forth like this:

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

So again... please don't do this. :) Definitely stick to the proper/sane way of doing things as in Steven's answer.

EDIT: Here's an implementation using expressions which works the exact same way in terms of syntax usage. Not sure if it's "correct", and haven't tested it against 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);
    }
}
like image 18
Chris Sinclair Avatar answered Oct 24 '22 09:10

Chris Sinclair