2013-08-29 9 views
27
Func<T, bool> expr = x => x.Prop != 1; 

somelist = somelist.Where(expr); 

Jak dotąd tak dobrze. Ale chciałbym negować expr takiego:Negating Func <T, bool> w wyrażeniu lambda

somelist = somelist.Where(!expr); 

co skutkuje błędem kompilacji: Cannot apply ! operator to operand of type Func<T, bool>.

Czy muszę w tym celu utworzyć inną zmienną wyrażenia?

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

Odpowiedz

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

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

lub

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

Przy użyciu wyrażenia drzew dodaje rade:

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

Aby ułatwić Ci życie, można utworzyć następujące metody rozszerzenie:

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>> predicate) 
{ 
    return Expression.Lambda<Func<T, bool>>(
     Expression.Not(expr.Body), 
     expr.Parameters); 
} 

Teraz można to zrobić:

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

Idę rzucić to tam jako głupie odpowiedź. Dla jasności: nie zrobiłbym tego i nie polecam nikomu tego robić. :)

Chciałem sprawdzić, czy można uzyskać składnię somelist.Where(!expr) lub coś podobnego.

Cóż, udało mi się, a ja nienawidzę siebie.

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

N.egatable był tylko mały pomocnik składnia wygoda i w dużej mierze niepotrzebne (EDIT: chciał aby uniknąć konieczności jednoznacznie określić MyClass lub w jakiś sposób dokonać konkretyzacji owijki obiektu ukrytego, ale nie potrafił się istnieje i że może ktoś będzie miał lepszy pomysł):

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

Negator<T> gdzie jest prawdziwy „magia” dzieje:

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

Najpierw przeciążenie operatora ! realizuje funkcję negacji (tak jak w przypadku this answer), następnie niejawny operator konwersji na Func<T, bool> pozwala przejść do metody rozszerzania Where.

Być może bardzo głupie to można zachować przerzucanie go tam iz powrotem tak:

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

Więc jeszcze raz ... proszę nie rób tego. :) Zdecydowanie trzymaj się właściwego/rozsądnego sposobu robienia rzeczy, jak w odpowiedzi Stevena.

EDYCJA: Oto implementacja za pomocą wyrażeń, które działają dokładnie w ten sam sposób pod względem użycia składni.Nie jestem pewien, czy jest to „poprawne”, a nie testowałem go przeciwko 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); 
    } 
} 
+0

Przepraszam cię torturować więcej, bo wiem, że to będzie prawdopodobnie jeść na ciebie, aż pojawi się ona pracować zbyt (byłem tam). Zastanawiam się, czy można go uruchomić z 'Wyrażeniem >' również, aby działało z dostawcami Linq2Entities, takimi jak Entity Framework. –

+1

@ScottChamberlain: Prawdopodobnie mógłbym to zrobić za pomocą wyrażeń, ale nie wiem, czy byłoby to zgodne z kompatybilnym wykonaniem _runtime_ dla encji (przypuszczam, że to _mrok_, w sumie, jakie są dodatkowe negacje dla zapytania SQL?). Może w wolnym czasie dam mu szansę. –

+0

Jeśli chcesz przekształcić lambdę w jej przeciwieństwo, możesz po prostu napisać, ze zmienną "Wyrażenie > originalLambda',' Wyrażenie > negatedLambda = originalLambda.Update (Expression.Not (originalLambda.Body), originalLambda .Parameters); ' –

Powiązane problemy