2009-03-30 6 views
13

The dynamic language runtime (DLR) ma pewne dość chłodnego kod dla ekspresji, w tym niektóre bardzo ładne kodu, aby wydrukować drzew ekspresyjne, które chcę użyć, tak aby:wydrukować Linq Expression drzewo hierarchii

int a = 1; 
int b = 2; 
Expression<Func<int, int>> expression = (c) => a + (b * c) 
expression.Evaluate(5, stringBuilder) 

Wyjścia:

(5) => a + (b * c) = 11 Where 
    a = 1 
    b * c = 10 Where 
      b = 2 
      c = 5 

Znalazłem kod w sieci, aby to zrobić, ale okazało się, że działa tylko wtedy, gdy wyrażenie nie zawiera żadnych argumentów.

http://incrediblejourneysintotheknown.blogspot.com/2009/02/displaying-nested-evaluation-tree-from.html

I wtedy odkrył realizację DLR podobną metodą. Jednak DLR ma własne niestandardowe implementacje klasy Expression i wiele innych standardowych typów C#, więc trochę się pogubiłem. Ktoś wie, w jaki sposób mogę wdrożyć powyższe?

Odpowiedz

13

Jak o:

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Text; 
using System.Text.RegularExpressions; 

static class Program 
{ 
    static void Main() 
    { 
     int a = 1, b = 2; 
     StringBuilder sb = new StringBuilder(); 
     Expression<Func<int, int>> expression = (c) => a + (b * c); 
     expression.Evaluate(sb, 5); 
     // now fix the capture class names (from a and b) 
     string s = sb.ToString(); 
     s = Regex.Replace(s, @"value\([^)]+\)\.", ""); 
     Console.WriteLine(s); 
    } 
    public static void Evaluate(this LambdaExpression expr, StringBuilder builder, params object[] args) 
    { 
     var parameters = expr.Parameters.ToArray(); 
     if (args == null || parameters.Length != args.Length) throw new ArgumentException("args"); 
     Evaluate(expr.Body, 0, builder, parameters, args); 
    } 
    private static StringBuilder Indent(this StringBuilder builder, int depth) 
    { 
     for (int i = 0; i < depth; i++) builder.Append(" "); 
     return builder; 
    } 
    private static void Evaluate(this Expression expr, int depth, StringBuilder builder, ParameterExpression[] parameters, object[] args) 
    { 
     builder.Indent(depth).Append(expr).Append(" = ").Append(Expression.Lambda(expr, parameters).Compile().DynamicInvoke(args)); 

     UnaryExpression ue; 
     BinaryExpression be; 
     ConditionalExpression ce; 

     if ((ue = expr as UnaryExpression) != null) 
     { 
      builder.AppendLine(" where"); 
      Evaluate(ue.Operand, depth + 1, builder, parameters, args); 
     } 
     if ((be = expr as BinaryExpression) != null) 
     { 
      builder.AppendLine(" where"); 
      Evaluate(be.Left, depth + 1, builder, parameters, args); 
      Evaluate(be.Right, depth + 1, builder, parameters, args);     
     } 
     else if ((ce = expr as ConditionalExpression) != null) 
     { 
      builder.AppendLine(" where"); 
      Evaluate(ce.Test, depth + 1, builder, parameters, args); 
      Evaluate(ce.IfTrue, depth + 1, builder, parameters, args); 
      Evaluate(ce.IfFalse, depth + 1, builder, parameters, args); 
     } 
     else 
     { 
      builder.AppendLine(); 
     } 
    } 

} 
+0

Bardzo dobrze, dziękuję. –

+1

W powyższym kodzie metody. Ocena i .Indent nie są rozpoznawane przez mój VS2012. Czego potrzebuję do zainstalowania lub dodania odnośnika? – user3057544

+0

To jest stare pytanie, ale aby odpowiedzieć na user3057544, prawdopodobnie lepiej jest umieścić to w klasie statycznej i odwołać się do niej jako metody rozszerzenia z LambdaExpression ... lub przynajmniej użyć go w klasach statycznych, jak to robi Marc powyżej. – dhysong