表达式树(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种表达式类型的解析。下面给出一些案例和完成的解析代码:
完整解析类:
// 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[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"); } } }
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
只要你掌握了规则,我相信这对你来说不是难事。
本文来自博客园,作者:LargeProb星辰,转载请注明原文链接:https://www.cnblogs.com/largeprob/p/18026782