表达式树,一种提高代码复用性的方式

一、问题源起

在有些情况下,我们的计算逻辑跟传入数据的内部结构有关系,不仅不同的数据的计算逻辑不同,即使同一种数据结构的计算逻辑也会随时间变化;例如我们的大数据收集系统,需要根据前方业务人员配置的过滤表达式,来决定数据是否可以入库;那么我们就需要这个筛选的逻辑既要有通用性,也需要保证执行的高效性;那么表达式树或许是一种可能的选项;

二、什么是表达式树

表达式树是以类似树的结构来表达代码逻辑的一种方式;其中每一个节点都是一个表达式,例如一个方法调用或者赋值语句等。

我们可以编译表达式树,然后可以像普通方法那样执行。使用表达式树,我们可以动态的修改代码的执行逻辑,同时也可以基于LINQ创建动态查询并在不同类型的数据上执行。

我们可以使用C#提供的System.Linq.Expressions下的类来手动创建表达式。

三、使用Lambda表达式创建表达式树

只有将Lambda表达式赋值给Expression类型的变量的时候,编译器会自动创建对应的表达式树;但是C#编译器只能为单句的Lambda表达式,这就大大限制了其使用的场景;

Expression<Func<int, bool>> lambda = num => num < 5; //生成的表达式树 .Lambda #Lambda1<System.Func`2[System.Int32,System.Boolean]>(System.Int32 $num) { $num < 5 } 

四、使用API创建表达式树

我们可以使用System.Linq.Expressions.Expression里提供的众多的静态工厂方法,根据需要创建不同类型的节点。

ParameterExpression numParam = Expression.Parameter(typeof(int), "num"); ConstantExpression five = Expression.Constant(5, typeof(int)); BinaryExpression numLessThanFive = Expression.LessThan(numParam, five); Expression<Func<int, bool>> lambda1 =Expression.Lambda<Func<int, bool>>( numLessThanFive, new ParameterExpression[] { numParam } ); //生成的表达式树 .Lambda #Lambda1<System.Func`2[System.Int32,System.Boolean]>(System.Int32 $num) { $num < 5 } 

我们可以使用System.Linq.Expressions.Expression里提供的赋值及流程控制的众多API,来实现更加复杂的代码逻辑。

// Creating a parameter expression. ParameterExpression value = Expression.Parameter(typeof(int), "value"); // Creating an expression to hold a local variable. ParameterExpression result = Expression.Parameter(typeof(int), "result"); // Creating a label to jump to from a loop. LabelTarget label = Expression.Label(typeof(int)); // Creating a method body. BlockExpression block = Expression.Block( // Adding a local variable. new[] { result }, // Assigning a constant to a local variable: result = 1 Expression.Assign(result, Expression.Constant(1)), // Adding a loop. Expression.Loop( // Adding a conditional block into the loop. Expression.IfThenElse( // Condition: value > 1 Expression.GreaterThan(value, Expression.Constant(1)), // If true: result *= value -- Expression.MultiplyAssign(result, Expression.PostDecrementAssign(value)), // If false, exit the loop and go to the label. Expression.Break(label, result) ), // Label to jump to. label ) ); // Compile and execute an expression tree. var factorial = Expression.Lambda<Func<int, int>>(block, value); //生成的表达式树 .Lambda #Lambda1<System.Func`2[System.Int32,System.Int32]>(System.Int32 $value) { .Block(System.Int32 $result) { $result = 1; .Loop { .If ($value > 1) { $result *= $value-- } .Else { .Break #Label1 { $result } } } .LabelTarget #Label1: } } 

五、编译表达式树

Expression提供的Compile方法可以把表达式树编译为可执行的委托。

// Creating an expression tree. Expression<Func<int, bool>> expr = num => num < 5; // Compiling the expression tree into a delegate. Func<int, bool> result = expr.Compile(); // Invoking the delegate and writing the result to the console. Console.WriteLine(result(4)); // Prints True. 

六、表达式树对方法关键部件的表达

我们有以下一个简单的方法,其中涉及方法的一些重要的基础部件

static int Sum(int x, int y) { int sum = x + y; return sum; } 

我们使用Expression.Parameter来声明需要传入参数的类型及名字;

var parax = Expression.Parameter(typeof(int), "x"); var paray = Expression.Parameter(typeof(int), "y"); 

我们使用Expression.Variable来声明执行过程中需要使用的局部变量;

var sum = Expression.Variable(typeof(int)); 

我们可以使用LableTarget、GotoExpression、LableExpression来实现方法的return;

static Expression<Func<int, int, int>> SumExpression() { var x = Expression.Parameter(typeof(int), "x"); var y = Expression.Parameter(typeof(int), "y"); var sum = Expression.Variable(typeof(int)); var add = Expression.Add(x, y); var assign = Expression.Assign(sum, add); var labelTarget = Expression.Label(typeof(int)); var ret = Expression.Return(labelTarget, sum); var labelExpression = Expression.Label(labelTarget, Expression.Constant(0)); var block = Expression.Block( new ParameterExpression[] { sum}, assign, ret, labelExpression ); return Expression.Lambda<Func<int, int, int>>(block, x, y); } //生成的表达式树 .Lambda #Lambda1<System.Func`3[System.Int32,System.Int32,System.Int32]>( System.Int32 $x, System.Int32 $y) { .Block(System.Int32 $var1) { $var1 = $x + $y; .Return #Label1 { $var1 }; .Label 0 .LabelTarget #Label1: } } 

七、构建获取JSON对象字段的值的表达式

构建表达式的时候传入想要获取值的字段名字,执行表达式的时候可以获取对应对象的字段值。

public static Expression<Func<JObject, string>> ValueExpression(string name, ParameterExpression source = null) { //JObject obj = null; //string name = null; //string result = null; //if (obj.ContainsKey(name)) //{ // var valueT = obj.GetValue(name); // result = valueT.ToObject<string>(); //} //return result; var result = Expression.Variable(typeof(string)); var paraObj = source ?? Expression.Parameter(typeof(JObject), "jObj"); var constName = Expression.Constant(name); var getValue = typeof(JObject).GetMethod("GetValue", new Type[] { typeof(string) }); var getValueCall = Expression.Call(paraObj, getValue, constName); var valueT = Expression.Variable(typeof(JToken)); var valueTAssign = Expression.Assign(valueT, getValueCall); var toObject = typeof(JToken).GetMethod("ToObject", new Type[] { }).MakeGenericMethod(typeof(string)); var toObjectCall = Expression.Call(valueT, toObject); var resultAssign = Expression.Assign(result, toObjectCall); var containBlock = Expression.Block( valueTAssign, resultAssign ); var contain = typeof(JObject).GetMethod("ContainsKey", new Type[] { typeof(string) }); var containCall = Expression.Call(paraObj, contain, constName); var containCondition = Expression.Condition(containCall, containBlock, Expression.Assign(result, Expression.Constant(string.Empty))); var target = Expression.Label(typeof(string)); var ret = Expression.Return(target, result); var block = Expression.Block( new ParameterExpression[] { result, valueT }, containCondition, ret, Expression.Label(target, Expression.Constant(string.Empty)) ); return Expression.Lambda<Func<JObject, string>>(block, paraObj); } 

八、构建Contain的表达式

构建的时候传入字段名字和测试是否包含的字符串;

public static Expression<Func<JObject, bool>> ContainsExpression(string name, string part, ParameterExpression source = null) { var result = Expression.Variable(typeof(bool)); var paraObj = source ?? Expression.Parameter(typeof(JObject), "jObj"); var constPart = Expression.Constant(part); var fieldValue = Expression.Variable(typeof(string)); var value = ValueExpression(name, paraObj).Body; var fieldValueAssign = Expression.Assign(fieldValue, value); var contains = typeof(string).GetMethod("Contains", new Type[] { typeof(string) }); var containsCall = Expression.Call(fieldValue, contains, constPart); var resultAssign = Expression.Assign(result, containsCall); var target = Expression.Label(typeof(bool)); var ret = Expression.Return(target, result); var block = Expression.Block( new ParameterExpression[] { result, fieldValue }, fieldValueAssign, resultAssign, ret, //Expression.Label(target, Expression.Constant(false)) Expression.Label(target, result) ); return Expression.Lambda<Func<JObject, bool>>(block, paraObj); } 

九、构建布尔表达式

根据实际的业务需要构建一个简单的布尔表达式

public static Expression<Func<JObject, bool>> BoolExpression() { var paraObj = Expression.Parameter(typeof(JObject), "jObj"); var aContains = ContainsExpression("name", "man", paraObj); var bContains = ContainsExpression("department", "dev", paraObj); var and = Expression.AndAlso(aContains.Body, bContains.Body); return Expression.Lambda<Func<JObject, bool>>(and, paraObj); } 

十、执行布尔表达式

static void Main(string[] args) { var obj = JObject.Parse("{" + "name:'mango'," + "department:'dev'"+ "}"); var e = BoolExpression(); var result = e.Compile()(obj); Console.WriteLine(result); Console.Read(); } //测试输出结果 true //生成的布尔表达式树 .Lambda #Lambda1<System.Func`2[Newtonsoft.Json.Linq.JObject,System.Boolean]>(Newtonsoft.Json.Linq.JObject $jObj) { .Block( System.Boolean $var1, System.String $var2) { $var2 = .Block( System.String $var3, Newtonsoft.Json.Linq.JToken $var4) { .If ( .Call $jObj.ContainsKey("name") ) { .Block() { $var4 = .Call $jObj.GetValue("name"); $var3 = .Call $var4.ToObject() } } .Else { $var3 = "" }; .Return #Label1 { $var3 }; .Label "" .LabelTarget #Label1: }; $var1 = .Call $var2.Contains("man"); .Return #Label2 { $var1 }; .Label $var1 .LabelTarget #Label2: } && .Block( System.Boolean $var5, System.String $var6) { $var6 = .Block( System.String $var7, Newtonsoft.Json.Linq.JToken $var8) { .If ( .Call $jObj.ContainsKey("department") ) { .Block() { $var8 = .Call $jObj.GetValue("department"); $var7 = .Call $var8.ToObject() } } .Else { $var7 = "" }; .Return #Label3 { $var7 }; .Label "" .LabelTarget #Label3: }; $var5 = .Call $var6.Contains("dev"); .Return #Label4 { $var5 }; .Label $var5 .LabelTarget #Label4: } } 
本网页由快兔兔AI采集器生成,目的为演示采集效果,若侵权请及时联系删除。

原文链接:https://www.cnblogs.com/wufengtinghai/p/15553543.html

更多内容