VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > c#编程 >
  • 表达式树(Expression)的执行、解释与创建

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

执行

这里需要说明下表达式树是表示代码的数据结构,并不是经过编译且可执行的代码,如果想要执行由表达式树表示的 .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}");

输出:

 

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

完整解析类:

 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<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

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


__EOF__

 
  • 本文作者: LargeProb星辰
  • 本文链接: https://www.cnblogs.com/largeprob/p/18026782

    
    相关教程