2011-12-16 16 views
11

Hãy nói rằng tôi có một biểu hiện như thế này:Thay đổi tên tham số trong một LambdaExpression chỉ để trưng bày

Expression<Predicate<T>> exp 

Nếu tôi gán biểu thức sau đây:

a => a.First() != 0 

và sau đó tôi gọi exp.ToString() tôi sẽ có được chính xác biểu thức tôi đã vượt qua, điều đó hoàn toàn tốt, nhưng, giả sử chúng ta muốn thay đổi tên chúng ta sử dụng cho 'a' bằng cái gì đó khác, làm sao chúng ta có thể làm được? Thay thế chuỗi sẽ không làm trong mọi trường hợp (nó hoạt động trong ví dụ trên, nhưng nếu tham số được gọi là 'i' chẳng hạn?) Có thể chỉ thay thế tên tham số, thời gian chạy, mà không ảnh hưởng đến ngữ nghĩa biểu thức?

CẬP NHẬT Các @PhilKlein hoạt động hoàn hảo, nhưng đòi hỏi khuôn khổ 4. Nhưng nếu chúng ta cần hướng đến các framework 3.5 chúng ta có thể sử dụng một lớp ExpressionVisitor từ Matt Warren, bởi chỉ modifing từ bảo vệ cho công chúng Visit phương pháp.

+0

Tôi không hiểu tại sao bạn muốn xuất ra một chuỗi biểu thị biểu thức mà nó không khớp chính xác với biểu thức. Tại sao bạn muốn làm nó? Tại sao * không * bạn muốn cấu trúc lại mã - tại sao nó phải xảy ra trong thời gian chạy? –

+0

@KirkWoll Tôi đang sử dụng biểu thức lambda làm siêu dữ liệu. Trong ngữ cảnh biểu thức được tạo ra, argumet có thể khác với cái tôi muốn hiển thị cho người dùng ở một điểm khác trong mã. Toàn bộ dự án nhỏ là một thư viện xác nhận đối số không sử dụng chuỗi phép thuật để hiển thị lỗi. –

Trả lời

8

Thật nhanh chóng và bẩn, nhưng giả sử bạn đang sử dụng .NET 4.0 bạn có thể tạo ra những điều sau đây:

public class PredicateRewriter 
{ 
    public static Expression<Predicate<T>> Rewrite<T>(Expression<Predicate<T>> exp, string newParamName) 
    { 
     var param = Expression.Parameter(exp.Parameters[0].Type, newParamName); 
     var newExpression = new PredicateRewriterVisitor(param).Visit(exp); 

     return (Expression<Predicate<T>>) newExpression; 
    } 

    private class PredicateRewriterVisitor : ExpressionVisitor 
    { 
     private readonly ParameterExpression _parameterExpression; 

     public PredicateRewriterVisitor(ParameterExpression parameterExpression) 
     { 
      _parameterExpression = parameterExpression; 
     } 

     protected override Expression VisitParameter(ParameterExpression node) 
     { 
      return _parameterExpression; 
     } 
    } 
} 

Và sau đó sử dụng nó như sau:

var newExp = PredicateRewriter.Rewrite(exp, "b"); 
newExp.ToString(); // returns "b => (b.First() == 0)" in your case 
+0

Cảm ơn, hoạt động như một sự quyến rũ. –

+0

Nhưng nó sẽ thay đổi tất cả các tham số thành "b". Nếu bạn chỉ muốn "a" được thay đổi thành "b", hãy xem giải pháp của tôi. – Krizz

0

Thông thường tôi sẽ sử dụng công cụ tái cấu trúc, chẳng hạn như Jetbrains Resharper để làm như vậy. Nó có một tính năng "Refactor, Rename" cho phép bạn làm điều đó, và biết sự khác biệt giữa một chuỗi thay thế và đổi tên biến. Tôi biết không có tính năng như vậy từ bên trong Visual Studio chính nó. http://www.jetbrains.com/resharper/

Nếu bạn đang đề cập đến việc xây dựng một biểu hiện năng động, tuy nhiên, và muốn thay đổi các thông số, bạn có thể sử dụng mã như sau (sao chép từ: c# List<string> to Lambda Expression with starter example: Refactor to handle the List)

// Create a parameter which passes the object 
    ParameterExpression param = Expression.Parameter(typeof(E), "x"); //x replaces a=> 

    // Create body of lambda expression 
    Expression body = Expression.PropertyOrField(param, fieldname); 

    // Create lambda function 
    Expression<Func<E, string>> exp = Expression.Lambda<Func<E, string>>(body, param); 

    // Compile it so we can use it 
    Func<E, string> orderFunc = exp.Compile(); 

Và để thay đổi các tham số từ "x" thành "y", chúng ta có thể làm như sau:

var newExpression = ReplaceFirstParameterName(exp, "y"); 

    private Expression<Func<E, string>>(Expression<Func<E,string>> exp, string newParamName) 
    { 
     var cloneParam = Expression.Parameter(exp.Parameters[0].Type, newParamName); 
     var body = exp.Body; 
     var newExp = Expression.Lambda<Func<string, string>>(body, cloneParam); 
     return newExp; 
    } 
+0

Tôi bỏ lỡ để xác định rằng phải được thực hiện thời gian chạy. –

+0

Dường như cách để đi (+1) nhưng ví dụ tạo ra một biểu thức mới từ chốt, mỗi mỏ trái đã được bật. –

+0

Mã ở trên hoạt động, sau đó, tuy nhiên nó không thực sự làm những gì bạn chỉ định - chuyển đổi chuỗi trở lại thành một biểu thức, và sau đó thay đổi tên tham số. Có một cuộc thảo luận khá dài ở đây về cách không có trình phân tích cú pháp biểu thức lambda từ một chuỗi, chủ yếu là vì có thể có vấn đề với (nếu tôi giải thích cuộc thảo luận chính xác) xung đột phạm vi biến: http://www.tech-archive.net/Archive /DotNet/microsoft.public.dotnet.languages.csharp/2007-05/msg04019.html Nếu nguồn của bạn không phải là một chuỗi cụ thể, bạn có thể thay đổi các tham số của đối tượng Expression của bạn. – JNadal

5

Expressions là không thay đổi như vậy, do đó, bạn không thể thay đổi chúng, bạn sẽ cần phải xây dựng cây mới.

Trong .NET 4.0, có một lớp học mà có thể giúp bạn đáng kể, xem ExpressionVisitor

Bạn có thể làm:

public class Renamer : ExpressionVisitor 
{ 
    public Expression Rename(Expression expression) 
    { 
     return Visit(expression); 
    } 

    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     if (node.Name == "a") 
      return Expression.Parameter(node.Type, "something_else"); 
     else 
      return node; 
    } 
} 

và sau đó, new Renamer().Rename(exp).ToString() nên giữ những gì bạn mong đợi.

+0

bạn là người khác đổi tên tất cả các tham số, nhưng đề xuất của bạn đã không làm việc ra khỏi hộp cho tôi: có lẽ vì VisitParameter không ghi đè? –

+0

có, đã được sửa. Cảm ơn bạn đã chỉ ra. – Krizz

+0

Tôi đã bỏ phiếu +1 trước đó vì vậy tôi không thể tăng lại, xin lỗi –

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