少林寺扫地的 发表于 2024-2-22 18:53:28

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

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

这里需要说明下表达式树是表示代码的数据结构,并不是经过编译且可执行的代码,如果想要执行由表达式树表示的 .NET 代码,必须将其转换为可执行的 IL 指令。这时候我们需要调用编译函数创建委托(Delegate),再执行。
执行编译后的委托通常看到三种方式:func(入参)、func.Invoke(入参)、func.DynamicInvoke(入参)
前两种用于在确认委托类型是可直接调用,DynamicInvoke一般在不确定委托的具体类型时调用
{
   //
   //定义Expression
   //@returnint
   //@paramsint(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
   //@returnint
   //@paramsUserInfo(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种表达式类型的解析。下面给出一些案例和完成的解析代码:
完整解析类:
// 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 + "\t\t");
      }
    }
}


//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);
            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 Codedemo1:(demo1的出输出太长无法贴图,建议读者自己尝试)
public class TestMonths
{
      publicint 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
只要你掌握了规则,我相信这对你来说不是难事。

来源:https://www.cnblogs.com/largeprob/p/18026782
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 表达式树(Expression)的执行、解释与创建