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

  • 表达式树(Expression)的执行、解释与创建已关闭评论
  • 33 次浏览
  • A+
所属分类:.NET技术
摘要

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

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

执行

这里需要说明下表达式树是表示代码的数据结构,并不是经过编译且可执行的代码,如果想要执行由表达式树表示的 .NET 代码,必须将其转换为可执行的 IL 指令。这时候我们需要调用编译函数创建委托(Delegate),再执行。

执行编译后的委托通常看到三种方式:func(入参)func.Invoke(入参)func.DynamicInvoke(入参)

前两种用于在确认委托类型是可直接调用,DynamicInvoke一般在不确定委托的具体类型时调用

 {      //        //  定义Expression      //  @return  int      //  @params  int(a),int(b)      //      Expression<Func<int, int, int>> expression = (a, b) => a + b;      //编译创建委托      Func<int, int, int> func = expression.Compile();      //入参执行      Console.WriteLine(func(1, 2));      Console.WriteLine(func.Invoke(1, 2));      Console.WriteLine(func.DynamicInvoke(1, 2));  }    {      //        //  创建Expression      //  @return  int      //  @params  UserInfo(user)      //      //入参表达式      var param = Expression.Parameter(typeof(UserInfo), "user");      //参数属性访问器表达式      var member = Expression.MakeMemberAccess(param, typeof(UserInfo).GetMember("Age").FirstOrDefault()!);      //常量表达式      var constant = Expression.Constant(999);      //二进制计算表达式      var body = Expression.Add(member, constant);      //Lambda表达式      var lambda = Expression.Lambda(body, param);      Console.WriteLine(lambda.Compile().DynamicInvoke(new UserInfo() { Age = 1 }));      //Console.WriteLine(((Func<UserInfo, int>)lambda.Compile()).DynamicInvoke(new UserInfo() { Age = 1 }));  }

注意Lambda 表达式将对表达式中引用的任何局部变量创建闭包,必须保证作为委托的一部分的任何变量在调用 Compile 的位置处和执行结果委托时可用,例如以下这种情况将会抛出异常

 public class Resource : IDisposable  {      private bool _isDisposed = false;      public int Argument      {          get          {              if (!_isDisposed)                  return 5;              else throw new ObjectDisposedException("资源已被释放");          }      }       public void Dispose()      {          _isDisposed = true;      }  }   {      var constant = new Resource();      Expression<Func<int, int>> expression = (b) => constant.Argument + b;      var func = expression.Compile();      try      {          //  释放后入参执行          constant.Dispose();          Console.WriteLine(func(1));      }      catch (Exception ex)      {          Console.WriteLine(ex);      // 异常      //System.ObjectDisposedException: Cannot access a disposed object.Object     name: '资源已被释放'      }  }

 

解释

解析表达式树能够让我们更好的理解和创建复杂的代码结构树,下面是一个简单的demo我直接从官网复制的。他解释了这个表达式的类型、参数、返回类型、以及表达式体的左右结构

  Expression<Func<int, int, int>> addition = (a, b) => a + b;   Console.WriteLine($"This expression is a {addition.NodeType} expression type");   Console.WriteLine($"The name of the lambda is {((addition.Name == null) ? "<null>" : addition.Name)}");   Console.WriteLine($"The return type is {addition.ReturnType.ToString()}");   Console.WriteLine($"The expression has {addition.Parameters.Count} arguments. They are:");   foreach (var argumentExpression in addition.Parameters)   {       Console.WriteLine($"tParameter Type: {argumentExpression.Type.ToString()}, Name: {argumentExpression.Name}");   }   var additionBody = (BinaryExpression)addition.Body;   Console.WriteLine($"The body is a {additionBody.NodeType} expression");   Console.WriteLine($"The left side is a {additionBody.Left.NodeType} expression");   var left = (ParameterExpression)additionBody.Left;   Console.WriteLine($"tParameter Type: {left.Type.ToString()}, Name: {left.Name}");   Console.WriteLine($"The right side is a {additionBody.Right.NodeType} expression");   var right = (ParameterExpression)additionBody.Right;   Console.WriteLine($"tParameter Type: {right.Type.ToString()}, Name: {right.Name}");

输出:

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

 

如上所示是一个很简单的表达式树,依次解析表达式树的本身信息->表达式结构体->结构体的左右表达式,在表达式中每一个参数变量都是一个表达式结构。如果想要解析更复杂的表达式,我们需要递归从外层依次从左往右剥离这个表达式树,在ExpressionType这个枚举中我们能看到所有的表达式类型(80余种),笔者自己在代码种匹配了27种表达式类型的解析。下面给出一些案例和完成的解析代码:

完整解析类:

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

// Base Visitor class: public abstract class Visitor {     private readonly Expression node;     protected Visitor(Expression node) => this.node = node;     public abstract void Visit(string prefix);     public ExpressionType NodeType => node.NodeType;      protected void ForegroundColor(ConsoleColor consoleColor, string key, bool isLine)     {         Console.ForegroundColor = consoleColor;         if (isLine)         {             Console.WriteLine(key);         }         else         {             Console.Write(key);         }         Console.ResetColor();     }      //27     public static Visitor CreateFromExpression(Expression node) =>      node.NodeType switch      {          ExpressionType.Lambda => new LambdaVisitor((LambdaExpression)node),           ExpressionType.Add => new BinaryVisitor((BinaryExpression)node),          ExpressionType.AddChecked => new BinaryVisitor((BinaryExpression)node),          ExpressionType.AddAssign => new BinaryVisitor((BinaryExpression)node),          ExpressionType.AddAssignChecked => new BinaryVisitor((BinaryExpression)node),                 ExpressionType.Subtract => new BinaryVisitor((BinaryExpression)node),          ExpressionType.SubtractChecked => new BinaryVisitor((BinaryExpression)node),          ExpressionType.SubtractAssign => new BinaryVisitor((BinaryExpression)node),          ExpressionType.SubtractAssignChecked => new BinaryVisitor((BinaryExpression)node),           ExpressionType.Multiply => new BinaryVisitor((BinaryExpression)node),          ExpressionType.MultiplyChecked => new BinaryVisitor((BinaryExpression)node),          ExpressionType.MultiplyAssign => new BinaryVisitor((BinaryExpression)node),          ExpressionType.MultiplyAssignChecked => new BinaryVisitor((BinaryExpression)node),           ExpressionType.Divide => new BinaryVisitor((BinaryExpression)node),          ExpressionType.DivideAssign => new BinaryVisitor((BinaryExpression)node),           ExpressionType.Modulo => new BinaryVisitor((BinaryExpression)node),          ExpressionType.ModuloAssign => new BinaryVisitor((BinaryExpression)node),           ExpressionType.GreaterThan => new BinaryVisitor((BinaryExpression)node),          ExpressionType.OrElse => new BinaryVisitor((BinaryExpression)node),           ExpressionType.Constant => new ConstantVisitor((ConstantExpression)node),              ExpressionType.Parameter => new ParameterVisitor((ParameterExpression)node),          ExpressionType.Equal => new BinaryVisitor((BinaryExpression)node),           ExpressionType.Conditional => new ConditionalVisitor((ConditionalExpression)node),          ExpressionType.Call => new MethodCallVisitor((MethodCallExpression)node),           ExpressionType.MemberAccess => new MemberAccessVisitor((MemberExpression)node),          ExpressionType.Convert => new ConvertVisitor((UnaryExpression)node),           ExpressionType.MemberInit => new NewVisitor((MemberInitExpression)node),                  _ => new BinaryVisitor((BinaryExpression)node),      }; }   //  New Visitor: 类型初始化 public class NewVisitor : Visitor {      private readonly MemberInitExpression node;     public NewVisitor(MemberInitExpression node) : base(node) => this.node = node;      public override void Visit(string prefix)     {         Console.Write($"{prefix}Expression Node Type:");         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);          Console.Write($"    =>  ");         ForegroundColor(ConsoleColor.Green, $"{node}", true);          Console.Write($"{prefix}New Type:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.NewExpression}", true);          Console.WriteLine($"{prefix}Members:");         foreach (MemberAssignment item in node.Bindings)         {             Console.Write($"{prefix}t");             ForegroundColor(ConsoleColor.DarkGray, $"{item.Member}", true);             var member = CreateFromExpression(item.Expression);             member.Visit(prefix + "tt");         }     } }   //  Convert Visitor: 一元运算符 public class ConvertVisitor : Visitor {     private readonly UnaryExpression node;     public ConvertVisitor(UnaryExpression node) : base(node) => this.node = node;      public override void Visit(string prefix)     {         Console.Write($"{prefix}Expression Node Type:");         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);          Console.Write($"    =>  ");         ForegroundColor(ConsoleColor.Green, $"{node}", true);          Console.Write($"{prefix}To Type:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.Type}", true);          Console.Write($"{prefix}To Type Body:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.Operand}", true);          var left = CreateFromExpression(node.Operand);         Console.WriteLine($"{prefix}Analysis Body:");         left.Visit(prefix + "t");     } }   //  MemberAccess Visitor: 属性访问器 public class MemberAccessVisitor : Visitor {     private readonly MemberExpression node;     public MemberAccessVisitor(MemberExpression node) : base(node) => this.node = node;      public override void Visit(string prefix)     {         Console.Write($"{prefix}Expression Node Type:");         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);          Console.Write($"    =>  ");         ForegroundColor(ConsoleColor.Green, $"{node}", true);           Console.Write($"{prefix}In:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.Member.DeclaringType}", true);          string type = node.Member.MemberType switch         {              MemberTypes.Event => ((EventInfo)node.Member).EventHandlerType.Name,             MemberTypes.Field => ((FieldInfo)node.Member).FieldType.Name,             MemberTypes.Method => ((MethodInfo)node.Member).ReturnType.Name,             MemberTypes.Property => ((PropertyInfo)node.Member).PropertyType.Name,             _ => throw new ArgumentException                  (                   "Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"                  )         };         Console.Write($"{prefix}Type:");         ForegroundColor(ConsoleColor.DarkGray, $"{type}", true);          Console.Write($"{prefix}Name:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.Member.Name}", true);     } }    // Lambda Visitor: Lambda表达式 public class LambdaVisitor : Visitor {     private readonly LambdaExpression node;     public LambdaVisitor(LambdaExpression node) : base(node) => this.node = node;     public override void Visit(string prefix)     {         Console.Write($"{prefix}Expression Node Type:");         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);          Console.Write($"    =>  ");         ForegroundColor(ConsoleColor.Green, $"{node}", true);          Console.Write($"{prefix}Name:");         ForegroundColor(ConsoleColor.DarkGray, $"{((node.Name == null) ? "<null>" : node.Name)}", true);          Console.Write($"{prefix}ReturnType:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.ReturnType}", true);          Console.Write($"{prefix}Parameters:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.Parameters.Count}", true);          for (int i = 0; i < node.Parameters.Count; i++)         {             var argumentVisitor = CreateFromExpression(node.Parameters[i]);             argumentVisitor.Visit(prefix + "t");         }          Console.WriteLine($"{prefix}Analysis Body:");         var bodyVisitor = CreateFromExpression(node.Body);         bodyVisitor.Visit(prefix + "t");     } }  // Binary Expression Visitor:二进制运算符 public class BinaryVisitor : Visitor {     private readonly BinaryExpression node;     public BinaryVisitor(BinaryExpression node) : base(node) => this.node = node;      public override void Visit(string prefix)     {         Console.Write($"{prefix}Expression Node Type:");         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);          Console.Write($"    =>  ");         ForegroundColor(ConsoleColor.Green, $"{node}", true);           var left = CreateFromExpression(node.Left);         Console.WriteLine($"{prefix}Left Analysis:");         left.Visit(prefix + "t");         var right = CreateFromExpression(node.Right);         Console.WriteLine($"{prefix}Right Analysis:");         right.Visit(prefix + "t");     } }    // Parameter visitor: 参数类型 public class ParameterVisitor : Visitor {     private readonly ParameterExpression node;     public ParameterVisitor(ParameterExpression node) : base(node)     {         this.node = node;     }      public override void Visit(string prefix)     {         Console.Write($"{prefix}Name:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.Name}", false);          Console.Write($"    Type:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.Type}", false);          Console.Write($"    ByRef:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.IsByRef}", true);     } }   // Constant visitor: 常数类型 public class ConstantVisitor : Visitor {     private readonly ConstantExpression node;     public ConstantVisitor(ConstantExpression node) : base(node) => this.node = node;     public override void Visit(string prefix)     {         Console.Write($"{prefix}Expression Node Type:");         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);          Console.Write($"    =>  ");         ForegroundColor(ConsoleColor.Green, $"{node}", true);          Console.Write($"{prefix}Type:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.Type}", true);          Console.Write($"{prefix}Value:");         ForegroundColor(ConsoleColor.DarkGray, $"{node.Value}", true);     } }  // Conditional visitor: 条件处理 public class ConditionalVisitor : Visitor {     private readonly ConditionalExpression node;     public ConditionalVisitor(ConditionalExpression node) : base(node)     {         this.node = node;     }     public override void Visit(string prefix)     {         Console.Write($"{prefix}Expression Node Type:");         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);          Console.Write($"    =>  ");         ForegroundColor(ConsoleColor.Green, $"{node}", true);          var testVisitor = Visitor.CreateFromExpression(node.Test);         Console.WriteLine($"{prefix}Analysis Test:");         testVisitor.Visit(prefix + "t");         var trueVisitor = Visitor.CreateFromExpression(node.IfTrue);         Console.WriteLine($"{prefix}Analysis IfTrue:");         trueVisitor.Visit(prefix + "t");         var falseVisitor = Visitor.CreateFromExpression(node.IfFalse);         Console.WriteLine($"{prefix}Analysis IfFalse:");         falseVisitor.Visit(prefix + "t");     } }  // MethodCall visitor: 方法调用 public class MethodCallVisitor : Visitor {     private readonly MethodCallExpression node;     public MethodCallVisitor(MethodCallExpression node) : base(node)     {         this.node = node;     }      public override void Visit(string prefix)     {         Console.Write($"{prefix}Expression Node Type:");         ForegroundColor(ConsoleColor.DarkBlue, $"{NodeType}", false);          Console.Write($"    =>  ");         ForegroundColor(ConsoleColor.Green, $"{node}", true);          Console.Write($"{prefix}Static or Receiver:");         if (node.Object == null)         {             ForegroundColor(ConsoleColor.DarkGray, $"static", true);         }         else         {             ForegroundColor(ConsoleColor.DarkGray, $"receiver (this)", true);             Console.WriteLine($"{prefix}Method Analysis:");             var receiverVisitor = Visitor.CreateFromExpression(node.Object);             receiverVisitor.Visit(prefix + "t");         }          var methodInfo = node.Method;         Console.Write($"{prefix}Method Name:");         ForegroundColor(ConsoleColor.DarkGray, $"{methodInfo.DeclaringType}.{methodInfo.Name}", true);          Console.WriteLine($"{prefix}Arguments:");         foreach (var arg in node.Arguments)         {             var argVisitor = Visitor.CreateFromExpression(arg);             argVisitor.Visit(prefix + "t");         }     }  }

View Code

demo1:(demo1的出输出太长无法贴图,建议读者自己尝试)

  public class TestMonths   {       public  int Conver32(int a)       {           return Convert.ToInt32(a);       }   }     TestMonths testMonths = new TestMonths();    var gg = 99;    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);    LambdaVisitor visitor = new LambdaVisitor(exception);    visitor.Visit("解析二进制运算:");    Console.WriteLine("t");

demo2:

 Expression<Func<UserInfo, UserInfoModel>> lambda = (user) => new UserInfoModel  {      Age = user.Age,      Level = EF.Functions.DataLength(user.Name) ?? 0,      Name = user.Name  };  LambdaVisitor visitor3 = new LambdaVisitor(lambda);  visitor3.Visit("解析查询表达式:");

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

 

创建

只有了解了他的构造,我们才懂得如何创建,下面给出两个例子分别是最常用的属性访问器和条件判断表达式

  //属性访问器   {       //需要构建的表达式       Expression<Func<UserInfo, int>> left = (user) => user.Age;        //1.创建访问对象表达式  =>(user)       var param = Expression.Parameter(typeof(UserInfo), "user");        //       //2.创建访问属性表达式 =>(user.Age)       //      @expression 我们需要访问成员的归属对象       //      @member     成员访问器       var member = Expression.MakeMemberAccess(param, typeof(UserInfo).GetMember("Age").FirstOrDefault()!);        //3.创建Lambda表达式       var lambda = Expression.Lambda(member, param);        //测试编译输出 18       Console.WriteLine(lambda.Compile().DynamicInvoke(new UserInfo { Age = 18 }));    }    //条件判断表达式   {       //需要构建的表达式       Expression<Func<UserInfo, bool>> left = (user) => user.Age > 9;        //1.创建访问对象表达式   =>(user)       var param = Expression.Parameter(typeof(UserInfo), "user");        //       //2.创建访问属性表达式   =>(user.Age)       //      @expression 我们需要访问成员的归属对象       //      @member     成员访问器       var member = Expression.MakeMemberAccess(param, typeof(UserInfo).GetMember("Age").FirstOrDefault()!);        //       //3.创建常量表达式    =>(9)       var constant = Expression.Constant(9);        //       //4.创建条件判断表达式    =>(>)       var greaterThan = Expression.GreaterThan(member, constant);        //5.创建Lambda表达式       var lambda = Expression.Lambda(greaterThan, param);        //测试编译输出 True       Console.WriteLine(lambda.Compile().DynamicInvoke(new UserInfo { Age = 18 }));   }

如上所述,通过解析表达式反推创建表达式树是一种不错的方法,还有更加复杂的创建方式不再介绍,笔者只提供思路。

在这里推荐一个不错的库可以直接将表达式树解释成C#或VB代码ExpressionTreeToString

只要你掌握了规则,我相信这对你来说不是难事。