翼度科技»论坛 编程开发 .net 查看内容

表达式树(Expression)的执行、解释与创建

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
前言:在这里不进行概念性描述,能看到这篇文章说明你已经知道如何使用表达式树
执行

这里需要说明下表达式树是表示代码的数据结构,并不是经过编译且可执行的代码,如果想要执行由表达式树表示的 .NET 代码,必须将其转换为可执行的 IL 指令。这时候我们需要调用编译函数创建委托(Delegate),再执行。
执行编译后的委托通常看到三种方式:func(入参)、func.Invoke(入参)、func.DynamicInvoke(入参)
前两种用于在确认委托类型是可直接调用,DynamicInvoke一般在不确定委托的具体类型时调用
  1. {
  2.      //  
  3.      //  定义Expression
  4.      //  @return  int
  5.      //  @params  int(a),int(b)
  6.      //
  7.      Expression<Func<int, int, int>> expression = (a, b) => a + b;
  8.      //编译创建委托
  9.      Func<int, int, int> func = expression.Compile();
  10.      //入参执行
  11.      Console.WriteLine(func(1, 2));
  12.      Console.WriteLine(func.Invoke(1, 2));
  13.      Console.WriteLine(func.DynamicInvoke(1, 2));
  14. }
  15. {
  16.      //  
  17.      //  创建Expression
  18.      //  @return  int
  19.      //  @params  UserInfo(user)
  20.      //
  21.      //入参表达式
  22.      var param = Expression.Parameter(typeof(UserInfo), "user");
  23.      //参数属性访问器表达式
  24.      var member = Expression.MakeMemberAccess(param, typeof(UserInfo).GetMember("Age").FirstOrDefault()!);
  25.      //常量表达式
  26.      var constant = Expression.Constant(999);
  27.      //二进制计算表达式
  28.      var body = Expression.Add(member, constant);
  29.      //Lambda表达式
  30.      var lambda = Expression.Lambda(body, param);
  31.      Console.WriteLine(lambda.Compile().DynamicInvoke(new UserInfo() { Age = 1 }));
  32.      //Console.WriteLine(((Func<UserInfo, int>)lambda.Compile()).DynamicInvoke(new UserInfo() { Age = 1 }));
  33. }
复制代码
注意Lambda 表达式将对表达式中引用的任何局部变量创建闭包,必须保证作为委托的一部分的任何变量在调用 Compile 的位置处和执行结果委托时可用,例如以下这种情况将会抛出异常
  1. public class Resource : IDisposable
  2. {
  3.      private bool _isDisposed = false;
  4.      public int Argument
  5.      {
  6.          get
  7.          {
  8.              if (!_isDisposed)
  9.                  return 5;
  10.              else throw new ObjectDisposedException("资源已被释放");
  11.          }
  12.      }
  13.      public void Dispose()
  14.      {
  15.          _isDisposed = true;
  16.      }
  17. }
  18. {
  19.      var constant = new Resource();
  20.      Expression<Func<int, int>> expression = (b) => constant.Argument + b;
  21.      var func = expression.Compile();
  22.      try
  23.      {
  24.          //  释放后入参执行
  25.          constant.Dispose();
  26.          Console.WriteLine(func(1));
  27.      }
  28.      catch (Exception ex)
  29.      {
  30.          Console.WriteLine(ex);
  31.      // 异常
  32.      //System.ObjectDisposedException: Cannot access a disposed object.Object     name: '资源已被释放'
  33.      }
  34. }
复制代码
 
解释

解析表达式树能够让我们更好的理解和创建复杂的代码结构树,下面是一个简单的demo我直接从官网复制的。他解释了这个表达式的类型、参数、返回类型、以及表达式体的左右结构
  1.   Expression<Func<int, int, int>> addition = (a, b) => a + b;
  2.   Console.WriteLine($"This expression is a {addition.NodeType} expression type");
  3.   Console.WriteLine($"The name of the lambda is {((addition.Name == null) ? "<null>" : addition.Name)}");
  4.   Console.WriteLine($"The return type is {addition.ReturnType.ToString()}");
  5.   Console.WriteLine($"The expression has {addition.Parameters.Count} arguments. They are:");
  6.   foreach (var argumentExpression in addition.Parameters)
  7.   {
  8.       Console.WriteLine($"\tParameter Type: {argumentExpression.Type.ToString()}, Name: {argumentExpression.Name}");
  9.   }
  10.   var additionBody = (BinaryExpression)addition.Body;
  11.   Console.WriteLine($"The body is a {additionBody.NodeType} expression");
  12.   Console.WriteLine($"The left side is a {additionBody.Left.NodeType} expression");
  13.   var left = (ParameterExpression)additionBody.Left;
  14.   Console.WriteLine($"\tParameter Type: {left.Type.ToString()}, Name: {left.Name}");
  15.   Console.WriteLine($"The right side is a {additionBody.Right.NodeType} expression");
  16.   var right = (ParameterExpression)additionBody.Right;
  17.   Console.WriteLine($"\tParameter Type: {right.Type.ToString()}, Name: {right.Name}");
复制代码
输出:

 
如上所示是一个很简单的表达式树,依次解析表达式树的本身信息->表达式结构体->结构体的左右表达式,在表达式中每一个参数变量都是一个表达式结构。如果想要解析更复杂的表达式,我们需要递归从外层依次从左往右剥离这个表达式树,在ExpressionType这个枚举中我们能看到所有的表达式类型(80余种),笔者自己在代码种匹配了27种表达式类型的解析。下面给出一些案例和完成的解析代码:
完整解析类:
  1. // Base Visitor class:
  2. public abstract class Visitor
  3. {
  4.     private readonly Expression node;
  5.     protected Visitor(Expression node) => this.node = node;
  6.     public abstract void Visit(string prefix);
  7.     public ExpressionType NodeType => node.NodeType;
  8.     protected void ForegroundColor(ConsoleColor consoleColor, string key, bool isLine)
  9.     {
  10.         Console.ForegroundColor = consoleColor;
  11.         if (isLine)
  12.         {
  13.             Console.WriteLine(key);
  14.         }
  15.         else
  16.         {
  17.             Console.Write(key);
  18.         }
  19.         Console.ResetColor();
  20.     }
  21.     //27
  22.     public static Visitor CreateFromExpression(Expression node) =>
  23.      node.NodeType switch
  24.      {
  25.          ExpressionType.Lambda => new LambdaVisitor((LambdaExpression)node),
  26.          ExpressionType.Add => new BinaryVisitor((BinaryExpression)node),
  27.          ExpressionType.AddChecked => new BinaryVisitor((BinaryExpression)node),
  28.          ExpressionType.AddAssign => new BinaryVisitor((BinaryExpression)node),
  29.          ExpressionType.AddAssignChecked => new BinaryVisitor((BinaryExpression)node),
  30.       
  31.          ExpressionType.Subtract => new BinaryVisitor((BinaryExpression)node),
  32.          ExpressionType.SubtractChecked => new BinaryVisitor((BinaryExpression)node),
  33.          ExpressionType.SubtractAssign => new BinaryVisitor((BinaryExpression)node),
  34.          ExpressionType.SubtractAssignChecked => new BinaryVisitor((BinaryExpression)node),
  35.          ExpressionType.Multiply => new BinaryVisitor((BinaryExpression)node),
  36.          ExpressionType.MultiplyChecked => new BinaryVisitor((BinaryExpression)node),
  37.          ExpressionType.MultiplyAssign => new BinaryVisitor((BinaryExpression)node),
  38.          ExpressionType.MultiplyAssignChecked => new BinaryVisitor((BinaryExpression)node),
  39.          ExpressionType.Divide => new BinaryVisitor((BinaryExpression)node),
  40.          ExpressionType.DivideAssign => new BinaryVisitor((BinaryExpression)node),
  41.          ExpressionType.Modulo => new BinaryVisitor((BinaryExpression)node),
  42.          ExpressionType.ModuloAssign => new BinaryVisitor((BinaryExpression)node),
  43.          ExpressionType.GreaterThan => new BinaryVisitor((BinaryExpression)node),
  44.          ExpressionType.OrElse => new BinaryVisitor((BinaryExpression)node),
  45.          ExpressionType.Constant => new ConstantVisitor((ConstantExpression)node),
  46.   
  47.          ExpressionType.Parameter => new ParameterVisitor((ParameterExpression)node),
  48.          ExpressionType.Equal => new BinaryVisitor((BinaryExpression)node),
  49.          ExpressionType.Conditional => new ConditionalVisitor((ConditionalExpression)node),
  50.          ExpressionType.Call => new MethodCallVisitor((MethodCallExpression)node),
  51.          ExpressionType.MemberAccess => new MemberAccessVisitor((MemberExpression)node),
  52.          ExpressionType.Convert => new ConvertVisitor((UnaryExpression)node),
  53.          ExpressionType.MemberInit => new NewVisitor((MemberInitExpression)node),
  54.       
  55.          _ => new BinaryVisitor((BinaryExpression)node),
  56.      };
  57. }
  58. //  New Visitor: 类型初始化
  59. public class NewVisitor : Visitor {
  60.     private readonly MemberInitExpression node;
  61.     public NewVisitor(MemberInitExpression node) : base(node) => this.node = node;
  62.     public override void Visit(string prefix)
  63.     {
  64.         Console.Write($"{prefix}Expression Node Type:");
  65.         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);
  66.         Console.Write($"    =>  ");
  67.         ForegroundColor(ConsoleColor.Green, $"{node}", true);
  68.         Console.Write($"{prefix}New Type:");
  69.         ForegroundColor(ConsoleColor.DarkGray, $"{node.NewExpression}", true);
  70.         Console.WriteLine($"{prefix}Members:");
  71.         foreach (MemberAssignment item in node.Bindings)
  72.         {
  73.             Console.Write($"{prefix}\t");
  74.             ForegroundColor(ConsoleColor.DarkGray, $"{item.Member}", true);
  75.             var member = CreateFromExpression(item.Expression);
  76.             member.Visit(prefix + "\t\t");
  77.         }
  78.     }
  79. }
  80. //  Convert Visitor: 一元运算符
  81. public class ConvertVisitor : Visitor
  82. {
  83.     private readonly UnaryExpression node;
  84.     public ConvertVisitor(UnaryExpression node) : base(node) => this.node = node;
  85.     public override void Visit(string prefix)
  86.     {
  87.         Console.Write($"{prefix}Expression Node Type:");
  88.         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);
  89.         Console.Write($"    =>  ");
  90.         ForegroundColor(ConsoleColor.Green, $"{node}", true);
  91.         Console.Write($"{prefix}To Type:");
  92.         ForegroundColor(ConsoleColor.DarkGray, $"{node.Type}", true);
  93.         Console.Write($"{prefix}To Type Body:");
  94.         ForegroundColor(ConsoleColor.DarkGray, $"{node.Operand}", true);
  95.         var left = CreateFromExpression(node.Operand);
  96.         Console.WriteLine($"{prefix}Analysis Body:");
  97.         left.Visit(prefix + "\t");
  98.     }
  99. }
  100. //  MemberAccess Visitor: 属性访问器
  101. public class MemberAccessVisitor : Visitor
  102. {
  103.     private readonly MemberExpression node;
  104.     public MemberAccessVisitor(MemberExpression node) : base(node) => this.node = node;
  105.     public override void Visit(string prefix)
  106.     {
  107.         Console.Write($"{prefix}Expression Node Type:");
  108.         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);
  109.         Console.Write($"    =>  ");
  110.         ForegroundColor(ConsoleColor.Green, $"{node}", true);
  111.         Console.Write($"{prefix}In:");
  112.         ForegroundColor(ConsoleColor.DarkGray, $"{node.Member.DeclaringType}", true);
  113.         string type = node.Member.MemberType switch
  114.         {
  115.             MemberTypes.Event => ((EventInfo)node.Member).EventHandlerType.Name,
  116.             MemberTypes.Field => ((FieldInfo)node.Member).FieldType.Name,
  117.             MemberTypes.Method => ((MethodInfo)node.Member).ReturnType.Name,
  118.             MemberTypes.Property => ((PropertyInfo)node.Member).PropertyType.Name,
  119.             _ => throw new ArgumentException
  120.                  (
  121.                   "Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"
  122.                  )
  123.         };
  124.         Console.Write($"{prefix}Type:");
  125.         ForegroundColor(ConsoleColor.DarkGray, $"{type}", true);
  126.         Console.Write($"{prefix}Name:");
  127.         ForegroundColor(ConsoleColor.DarkGray, $"{node.Member.Name}", true);
  128.     }
  129. }
  130. // Lambda Visitor: Lambda表达式
  131. public class LambdaVisitor : Visitor
  132. {
  133.     private readonly LambdaExpression node;
  134.     public LambdaVisitor(LambdaExpression node) : base(node) => this.node = node;
  135.     public override void Visit(string prefix)
  136.     {
  137.         Console.Write($"{prefix}Expression Node Type:");
  138.         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);
  139.         Console.Write($"    =>  ");
  140.         ForegroundColor(ConsoleColor.Green, $"{node}", true);
  141.         Console.Write($"{prefix}Name:");
  142.         ForegroundColor(ConsoleColor.DarkGray, $"{((node.Name == null) ? "<null>" : node.Name)}", true);
  143.         Console.Write($"{prefix}ReturnType:");
  144.         ForegroundColor(ConsoleColor.DarkGray, $"{node.ReturnType}", true);
  145.         Console.Write($"{prefix}Parameters:");
  146.         ForegroundColor(ConsoleColor.DarkGray, $"{node.Parameters.Count}", true);
  147.         for (int i = 0; i < node.Parameters.Count; i++)
  148.         {
  149.             var argumentVisitor = CreateFromExpression(node.Parameters[i]);
  150.             argumentVisitor.Visit(prefix + "\t");
  151.         }
  152.         Console.WriteLine($"{prefix}Analysis Body:");
  153.         var bodyVisitor = CreateFromExpression(node.Body);
  154.         bodyVisitor.Visit(prefix + "\t");
  155.     }
  156. }
  157. // Binary Expression Visitor:二进制运算符
  158. public class BinaryVisitor : Visitor
  159. {
  160.     private readonly BinaryExpression node;
  161.     public BinaryVisitor(BinaryExpression node) : base(node) => this.node = node;
  162.     public override void Visit(string prefix)
  163.     {
  164.         Console.Write($"{prefix}Expression Node Type:");
  165.         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);
  166.         Console.Write($"    =>  ");
  167.         ForegroundColor(ConsoleColor.Green, $"{node}", true);
  168.         var left = CreateFromExpression(node.Left);
  169.         Console.WriteLine($"{prefix}Left Analysis:");
  170.         left.Visit(prefix + "\t");
  171.         var right = CreateFromExpression(node.Right);
  172.         Console.WriteLine($"{prefix}Right Analysis:");
  173.         right.Visit(prefix + "\t");
  174.     }
  175. }
  176. // Parameter visitor: 参数类型
  177. public class ParameterVisitor : Visitor
  178. {
  179.     private readonly ParameterExpression node;
  180.     public ParameterVisitor(ParameterExpression node) : base(node)
  181.     {
  182.         this.node = node;
  183.     }
  184.     public override void Visit(string prefix)
  185.     {
  186.         Console.Write($"{prefix}Name:");
  187.         ForegroundColor(ConsoleColor.DarkGray, $"{node.Name}", false);
  188.         Console.Write($"    Type:");
  189.         ForegroundColor(ConsoleColor.DarkGray, $"{node.Type}", false);
  190.         Console.Write($"    ByRef:");
  191.         ForegroundColor(ConsoleColor.DarkGray, $"{node.IsByRef}", true);
  192.     }
  193. }
  194. // Constant visitor: 常数类型
  195. public class ConstantVisitor : Visitor
  196. {
  197.     private readonly ConstantExpression node;
  198.     public ConstantVisitor(ConstantExpression node) : base(node) => this.node = node;
  199.     public override void Visit(string prefix)
  200.     {
  201.         Console.Write($"{prefix}Expression Node Type:");
  202.         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);
  203.         Console.Write($"    =>  ");
  204.         ForegroundColor(ConsoleColor.Green, $"{node}", true);
  205.         Console.Write($"{prefix}Type:");
  206.         ForegroundColor(ConsoleColor.DarkGray, $"{node.Type}", true);
  207.         Console.Write($"{prefix}Value:");
  208.         ForegroundColor(ConsoleColor.DarkGray, $"{node.Value}", true);
  209.     }
  210. }
  211. // Conditional visitor: 条件处理
  212. public class ConditionalVisitor : Visitor
  213. {
  214.     private readonly ConditionalExpression node;
  215.     public ConditionalVisitor(ConditionalExpression node) : base(node)
  216.     {
  217.         this.node = node;
  218.     }
  219.     public override void Visit(string prefix)
  220.     {
  221.         Console.Write($"{prefix}Expression Node Type:");
  222.         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);
  223.         Console.Write($"    =>  ");
  224.         ForegroundColor(ConsoleColor.Green, $"{node}", true);
  225.         var testVisitor = Visitor.CreateFromExpression(node.Test);
  226.         Console.WriteLine($"{prefix}Analysis Test:");
  227.         testVisitor.Visit(prefix + "\t");
  228.         var trueVisitor = Visitor.CreateFromExpression(node.IfTrue);
  229.         Console.WriteLine($"{prefix}Analysis IfTrue:");
  230.         trueVisitor.Visit(prefix + "\t");
  231.         var falseVisitor = Visitor.CreateFromExpression(node.IfFalse);
  232.         Console.WriteLine($"{prefix}Analysis IfFalse:");
  233.         falseVisitor.Visit(prefix + "\t");
  234.     }
  235. }
  236. // MethodCall visitor: 方法调用
  237. public class MethodCallVisitor : Visitor
  238. {
  239.     private readonly MethodCallExpression node;
  240.     public MethodCallVisitor(MethodCallExpression node) : base(node)
  241.     {
  242.         this.node = node;
  243.     }
  244.     public override void Visit(string prefix)
  245.     {
  246.         Console.Write($"{prefix}Expression Node Type:");
  247.         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);
  248.         Console.Write($"    =>  ");
  249.         ForegroundColor(ConsoleColor.Green, $"{node}", true);
  250.         Console.Write($"{prefix}Static or Receiver:");
  251.         if (node.Object == null)
  252.         {
  253.             ForegroundColor(ConsoleColor.DarkGray, $"static", true);
  254.         }
  255.         else
  256.         {
  257.             ForegroundColor(ConsoleColor.DarkGray, $"receiver (this)", true);
  258.             Console.WriteLine($"{prefix}Method Analysis:");
  259.             var receiverVisitor = Visitor.CreateFromExpression(node.Object);
  260.             receiverVisitor.Visit(prefix + "\t");
  261.         }
  262.         var methodInfo = node.Method;
  263.         Console.Write($"{prefix}Method Name:");
  264.         ForegroundColor(ConsoleColor.DarkGray, $"{methodInfo.DeclaringType}.{methodInfo.Name}", true);
  265.         Console.WriteLine($"{prefix}Arguments:");
  266.         foreach (var arg in node.Arguments)
  267.         {
  268.             var argVisitor = Visitor.CreateFromExpression(arg);
  269.             argVisitor.Visit(prefix + "\t");
  270.         }
  271.     }
  272. }
复制代码
View Codedemo1:(demo1的出输出太长无法贴图,建议读者自己尝试)
  1.   public class TestMonths
  2.   {
  3.       public  int Conver32(int a)
  4.       {
  5.           return Convert.ToInt32(a);
  6.       }
  7.   }
  8.    TestMonths testMonths = new TestMonths();
  9.    var gg = 99;
  10.    Expression<Func<UserInfo, int, int, double>> exception = (user, a, b) => user.Age + (a / b) * a - b + a - (b + b) + (a % b) + (a > b ? a : b) / user.Level * gg + testMonths.Conver32(user.Level);
  11.    LambdaVisitor visitor = new LambdaVisitor(exception);
  12.    visitor.Visit("解析二进制运算:");
  13.    Console.WriteLine("\t");
复制代码
demo2:
  1. Expression<Func<UserInfo, UserInfoModel>> lambda = (user) => new UserInfoModel
  2. {
  3.      Age = user.Age,
  4.      Level = EF.Functions.DataLength(user.Name) ?? 0,
  5.      Name = user.Name
  6. };
  7. LambdaVisitor visitor3 = new LambdaVisitor(lambda);
  8. visitor3.Visit("解析查询表达式:");
复制代码

 
创建

只有了解了他的构造,我们才懂得如何创建,下面给出两个例子分别是最常用的属性访问器和条件判断表达式
  1.   //属性访问器
  2.   {
  3.       //需要构建的表达式
  4.       Expression<Func<UserInfo, int>> left = (user) => user.Age;
  5.       //1.创建访问对象表达式  =>(user)
  6.       var param = Expression.Parameter(typeof(UserInfo), "user");
  7.       //
  8.       //2.创建访问属性表达式 =>(user.Age)
  9.       //      @expression 我们需要访问成员的归属对象
  10.       //      @member     成员访问器
  11.       var member = Expression.MakeMemberAccess(param, typeof(UserInfo).GetMember("Age").FirstOrDefault()!);
  12.       //3.创建Lambda表达式
  13.       var lambda = Expression.Lambda(member, param);
  14.       //测试编译输出 18
  15.       Console.WriteLine(lambda.Compile().DynamicInvoke(new UserInfo { Age = 18 }));
  16.   }
  17.   //条件判断表达式
  18.   {
  19.       //需要构建的表达式
  20.       Expression<Func<UserInfo, bool>> left = (user) => user.Age > 9;
  21.       //1.创建访问对象表达式   =>(user)
  22.       var param = Expression.Parameter(typeof(UserInfo), "user");
  23.       //
  24.       //2.创建访问属性表达式   =>(user.Age)
  25.       //      @expression 我们需要访问成员的归属对象
  26.       //      @member     成员访问器
  27.       var member = Expression.MakeMemberAccess(param, typeof(UserInfo).GetMember("Age").FirstOrDefault()!);
  28.       //
  29.       //3.创建常量表达式    =>(9)
  30.       var constant = Expression.Constant(9);
  31.       //
  32.       //4.创建条件判断表达式    =>(>)
  33.       var greaterThan = Expression.GreaterThan(member, constant);
  34.       //5.创建Lambda表达式
  35.       var lambda = Expression.Lambda(greaterThan, param);
  36.       //测试编译输出 True
  37.       Console.WriteLine(lambda.Compile().DynamicInvoke(new UserInfo { Age = 18 }));
  38.   }
复制代码
如上所述,通过解析表达式反推创建表达式树是一种不错的方法,还有更加复杂的创建方式不再介绍,笔者只提供思路。
在这里推荐一个不错的库可以直接将表达式树解释成C#或VB代码ExpressionTreeToString
只要你掌握了规则,我相信这对你来说不是难事。

来源:https://www.cnblogs.com/largeprob/p/18026782
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具