2012-12-03 35 views
9

Tôi cần tạo biểu thức LINQ động mà tôi bắt đầu làm việc với nhiều ví dụ. Tôi đã thử nghiệm một số và một số công việc và một số không. Trong trường hợp này tôi muốn tạo ra một phương pháp mà trông giống như:Biểu thức LINQ động với giá trị trả về

public bool Check(int intvar) 
{ 
    if (i > 2) 
    return true; 
    else 
    return false; 
} 

Bây giờ tôi đã viết như sau:

LabelTarget returnTarget = Expression.Label("label"); 
ParameterExpression para = Expression.Parameter(typeof(int), "intvalue"); 
Expression test = Expression.GreaterThan(para, Expression.Constant(5)); 
Expression iftrue = Expression.Return(returnTarget, Expression.Constant(true)); 
Expression iffalse = Expression.Return(returnTarget,     Expression.Constant(false)); 
Expression.IfThenElse(test, iftrue, iffalse); 

this.TheExpression = Expression.IfThenElse(test, iftrue, iffalse); 
Expression.Lambda<Action<int>>(
this.TheExpression, 
new ParameterExpression[] { para } 
).Compile()(5); 

Bây giờ nó ném InvalidOperationException:

Không thể nhảy đến nhãn "label" `

Điều gì là sai? Tôi chỉ cần trả lại đúng hoặc sai.

+0

Bạn có thể cho chúng tôi biết thêm một chút về những gì bạn đang cố gắng để đạt được? Ví dụ: tại sao bạn cần phải tự động tạo biểu thức này ... và tại sao bạn cần sử dụng nhãn và nếu \ else khi bạn có thể viết: kiểm tra công khai bool (int intvar) { trả lại i> 2; } –

Trả lời

14

Bạn cần phải thay đổi một vài điều:

  • Đặt nhãn trở lại ở dưới cùng của chức năng của bạn trong một biểu thức khối, như René gợi ý. Đây là nơi câu lệnh return của bạn sẽ nhảy.

  • Khai báo Lambda là loại Func<int, bool>. Vì bạn muốn có một giá trị trả về, điều này cần phải là một hàm, chứ không phải là một hành động.

  • Khai báo nhãn returnTarget là loại bool. Vì giá trị trả về của biểu thức khối là giá trị của câu lệnh cuối cùng của nó, nhãn phải đúng loại.

  • Cung cấp giá trị mặc định cho nhãn cuối cùng (= giá trị trả về của hàm nếu nhãn đạt được bằng luồng điều khiển thông thường thay vì câu lệnh return).

    LabelTarget returnTarget = Expression.Label(typeof(bool)); 
    ParameterExpression para = Expression.Parameter(typeof(int), "intvalue"); 
    Expression test = Expression.GreaterThan(para, Expression.Constant(5)); 
    Expression iftrue = Expression.Return(returnTarget, Expression.Constant(true)); 
    Expression iffalse = Expression.Return(returnTarget, Expression.Constant(false)); 
    
    var ex = Expression.Block(
        Expression.IfThenElse(test, iftrue, iffalse), 
        Expression.Label(returnTarget, Expression.Constant(false))); 
    
    var compiled = Expression.Lambda<Func<int, bool>>(
        ex, 
        new ParameterExpression[] { para } 
    ).Compile(); 
    
    Console.WriteLine(compiled(5));  // prints "False" 
    Console.WriteLine(compiled(6));  // prints "True" 
    
+1

Cảm ơn. Làm tốt lắm. Vì vậy, các nhà điều hành goto được tái sinh. ;-). – sven

+6

Nếu bạn nhìn vào một số mã IL biên dịch, bạn có thể thấy rằng nó không bao giờ thực sự còn lại ...;) –

1

returnTarget hiện chỉ được tham chiếu bởi câu lệnh if/then/else của bạn. Nhãn không được đặt trong câu lệnh ở bất kỳ đâu. Vì vậy, nó không biết nơi để nhảy đến. Nhãn chỉ được xác định và tham chiếu, nhưng không được đặt.

Hãy thử sử dụng Expression.Block để kết hợp lambda và nhãn của bạn.

Expression.Lambda<Action<int>>(
    Expression.Block(
     this.TheExpression, 
     Expression.Label(returnTarget) 
    ), 
    new ParameterExpression[] { para } 
    ).Compile()(5); 

Chưa thử nghiệm, nhưng đây là hướng chung mà bạn có thể tìm thấy câu trả lời.

-update- kiểm tra nó, lambda ở trên biên dịch và chạy tốt như bây giờ.

-update2- cách ly, bạn cũng muốn trả lại giá trị, ít nhất hãy để tôi xem, nó phải là Func thay vì Action.

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