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

somelist = somelist.Where(expr); 

Cho đến nay rất tốt. Nhưng tôi muốn phủ nhận expr như thế này:Phủ nhận Func <T, bool> trong biểu thức lambda

somelist = somelist.Where(!expr); 

nào gây ra một lỗi biên dịch: Cannot apply ! operator to operand of type Func<T, bool>.

Tôi có phải tạo biến biểu thức khác cho điều này không?

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

Trả lời

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

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

hoặc

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

Khi sử dụng cây biểu thức sau đây sẽ làm các 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); 

Để làm cho cuộc sống của bạn dễ dàng hơn, bạn có thể tạo ra các phương pháp khuyến nông như sau:

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

Bây giờ bạn có thể làm điều này:

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

tôi chỉ cần đi để ném này ngoài kia như một ngớ ngẩn câu trả lời . Chỉ cần được rõ ràng: Tôi sẽ không làm điều này, và tôi không khuyên rằng bất cứ ai làm điều này. :)

Tôi muốn xem liệu có thể nhận cú pháp somelist.Where(!expr) hoặc một cái gì đó tương tự hay không.

Tôi đã thành công và tôi ghét bản thân mình.

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

Các N.egatable chỉ là một helper cú pháp tiện lợi nhỏ và phần lớn không cần thiết (EDIT: Tôi muốn để tránh việc phải xác định một cách rõ ràng MyClass hoặc bằng cách nào đó làm cho instantiation của wrapper đối tượng ẩn, nhưng có thể không hoàn toàn nhận được ở đó và nghĩ có lẽ ai đó sẽ có một ý tưởng tốt hơn):

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

Negator<T> là nơi thực sự "kỳ diệu" sẽ xảy ra:

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

Trước tiên quá tải vận hành ! thực hiện chức năng phủ định (giống như trong this answer), thì toán tử chuyển đổi ẩn thành Func<T, bool> cho phép nó chuyển sang phương thức mở rộng Where.

Có lẽ rất ngớ ngẩn là bạn có thể giữ nó lật qua lại như thế này:

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

Vì vậy, một lần nữa ... xin đừng làm điều này. :) Chắc chắn dính vào cách thích hợp/sane làm những việc như trong câu trả lời của Steven.

EDIT: Đây là triển khai sử dụng các biểu thức hoạt động theo cùng một cách chính xác về cách sử dụng cú pháp.Không chắc chắn nếu đó là "chính xác", và chưa thử nghiệm nó chống lại 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

Xin lỗi bạn đã tra tấn bạn vì tôi biết điều này có thể sẽ ăn vào bạn cho đến khi bạn làm việc đó (tôi đã ở đó). Tôi tự hỏi nếu bạn có thể làm cho nó hoạt động với một 'Expression >' quá để làm cho nó hoạt động với các nhà cung cấp LINq2Entities như Entity Framework. –

+1

@ScottChamberlain: Tôi có thể làm điều đó với các biểu thức, nhưng tôi không biết liệu nó có chuyển thành thực thi _runtime_ tương thích cho các thực thể hay không (tôi giả sử nó _might_, sau cùng thì, thêm một vài sự phủ định cho truy vấn SQL là gì?). Có lẽ trong thời gian rảnh rỗi của tôi tôi sẽ cho nó một shot. –

+0

Nếu bạn muốn chuyển đổi một lambda vào đối diện của nó, bạn chỉ có thể viết, với biến 'Expression > originalLambda',' Expression > negatedLambda = originalLambda.Update (Expression.Not (originalLambda.Body), originalLambda .Parameters); ' –

Các vấn đề liên quan