C#简单实现表达式目录树(Expression)
1.什么是表达式目录树 :简单的说是一种语法树,或者说是一种数据结构(expression)
2.用lambda声明表达式目录树:
expression<func<int, int, int>> exp = (n, m) => n * m + 2; //表达试目录树的方法体只能是一行,不能有大括号。比如: //expression<func<int, int, int>> exp1 = (m, n) => // { // return m * n + 2; // };
3.expression.compile();
func<int, int, int> func = (m, n) => m * n + 2; expression<func<int, int, int>> exp = (m, n) => m * n + 2; int iresult1 = func.invoke(99, 99); int iresult2 = exp.compile().invoke(99, 99);
iresult1 和iresult2的结果一样,但是能compile()的只有lambdaexpression。 compile() 是将表达式树描述的 lambda 表达式编译为可执行代码,并生成表示该 lambda 表达式的委托。exp.compile().invoke(99,99) 相当于这样调用 exp.compile()();
4.認識表达式目录树结构。把上面的表达式拆分就是如下图,小学数学知识里的,按照运算符优先级别,先算乘法,m*n,得出结果再算加法,加上2。
如代码所示,m和n是参数,所以类型为parameterexpression ,2是常量,常量类型是constantexpression ,multiplyassign 乘法,add加法。第六步中只能执行表示lambda表达式的表达式目录树,即lambdaexpression或者expression<tdelegate>类型。如果表达式目录树不是表示lambda表达式,需要调用lambda方法创建一个新的表达式。actexpression.compile()成委托,再调用。
{ parameterexpression left = expression.parameter(typeof(int), "m");//左边的参数 parameterexpression right = expression.parameter(typeof(int), "n");//右边的参数 constantexpression constantlexp = expression.constant(2,typeof(int));//常量2 binaryexpression binaryexpmult = expression.multiplyassign(left, right);//两个参数相乘 binaryexpression binaryexpadd=expression.add(binaryexpmult, constantlexp);//相乘的结果再加2 expression<func<int, int,int>> actexpression = expression.lambda<func<int, int, int>>(binaryexpadd, left, right); int result= actexpression.compile()(2, 1);//调用 console.writeline(result+""); }
一些表达式目录树常用的类型
5.表达式目录树+缓存
using system; using system.collections.generic; using system.linq; using system.text; using system.threading.tasks; namespace threehomework.model { public class student { public int id { get; set; } public string name { get; set; } public int age { get; set; } } public class studentdto { public int id { get; set; } public string name { get; set; } public int age { get; set; } } }
有时候一些业务模型和实体模型不太一样,比如student 于studentdto实体的转换
一般的写法,new 一个实体然后把值赋给另一个实体,有一个就写一个,有十个就写是个,代码写死了,硬编码性能高
{ student student = new student() { age = 12, id=1, name="晴天" }; studentdto studentdto = new studentdto() { name = student.name, id = student.id, age = student.age }; }
第二种:使用expression表达式目录树
expression<func<student, studentdto>> lambda = p => new studentdto { age = p.age, id = p.id, name = p.name }; lambda.compile().invoke(student);
01.使用字典缓存表达式树,第一步是实例化了一个命令参数,parameterexpression, list<memberbinding> memberbindinglist = new list<memberbinding>();
是一个对象成员集合列表,循环tout的所有公共的属性和字段,add到memberbindinglist集合中,然后使用memberinitexpression初始化多个对象拼装再调用。第一次调用动态拼装,组装了一个key放入字典中,缓存之后,就直接调用字典中的数据。缓存后的就是硬编码所以性能高。
using system; using system.collections.generic; using system.linq; using system.linq.expressions; using system.text; using system.threading.tasks; namespace threehomework.mappingextend { /// <summary> /// 生成表达式目录树。字典缓存 /// </summary> public class expressionmapper { private static dictionary<string, object> _dic = new dictionary<string, object>(); /// <summary> /// 字典缓存表达式树 /// </summary> /// <typeparam name="tin"></typeparam> /// <typeparam name="tout"></typeparam> /// <param name="tin"></param> /// <returns></returns> public static tout trans<tin, tout>(tin tin) { string key = string.format("funckey_{0}_{1}", typeof(tin).fullname, typeof(tout).fullname); if (!_dic.containskey(key)) { parameterexpression parameterexpression = expression.parameter(typeof(tin), "p"); list<memberbinding> memberbindinglist = new list<memberbinding>(); foreach (var item in typeof(tout).getproperties()) { memberexpression property = expression.property(parameterexpression, typeof(tin).getproperty(item.name)); memberbinding memberbinding = expression.bind(item, property); memberbindinglist.add(memberbinding); } foreach (var item in typeof(tout).getfields()) { memberexpression property = expression.field(parameterexpression, typeof(tin).getfield(item.name)); memberbinding memberbinding = expression.bind(item, property); memberbindinglist.add(memberbinding); } memberinitexpression memberinitexpression = expression.memberinit(expression.new(typeof(tout)), memberbindinglist.toarray()); expression<func<tin, tout>> lambda = expression.lambda<func<tin, tout>>(memberinitexpression, new parameterexpression[] { parameterexpression }); func<tin, tout> func = lambda.compile();//拼装是一次性的 _dic[key] = func; } return ((func<tin, tout>)_dic[key]).invoke(tin); } } }
02.泛型+反射,接收一个tin类型的,返回一个tout类型的反射,通过反射遍历赋值。
using system; using system.collections.generic; using system.linq; using system.text; using system.threading.tasks; namespace threehomework.mappingextend { public class reflectionmapper { /// <summary> /// 反射 /// </summary> /// <typeparam name="tin"></typeparam> /// <typeparam name="tout"></typeparam> /// <param name="tin"></param> /// <returns></returns> public static tout trans<tin, tout>(tin tin) { tout tout = activator.createinstance<tout>();//创建对象 foreach (var itemout in tout.gettype().getproperties())//遍历属性 { foreach (var itemin in tin.gettype().getproperties()) { if (itemout.name.equals(itemin.name)) { itemout.setvalue(tout, itemin.getvalue(tin)); break; } } } foreach (var itemout in tout.gettype().getfields())//遍历字段 { foreach (var itemin in tin.gettype().getfields()) { if (itemout.name.equals(itemin.name)) { itemout.setvalue(tout, itemin.getvalue(tin)); break; } } } return tout; } } }
03.使用第三方序列化反序列化工具,newtonsoft.json是比较好的一个工具,这种方式序列化代码虽然一行搞定,但是序列化和反序列化的动作比反射动作大点,耗时会比较高。
using newtonsoft.json; using system; using system.collections.generic; using system.linq; using system.text; using system.threading.tasks; namespace expressiondemo.mappingextend { public class serializemapper { /// <summary> /// 序列化反序列化方式 /// </summary> /// <typeparam name="tin"></typeparam> /// <typeparam name="tout"></typeparam> public static tout trans<tin, tout>(tin tin) { return jsonconvert.deserializeobject<tout>(jsonconvert.serializeobject(tin)); } } }
04.生成表达式目录树,泛型缓存,使用泛型缓存性能是最高的。动态实现student与studentdto的转换。
using system; using system.collections.generic; using system.linq; using system.linq.expressions; using system.text; using system.threading.tasks; namespace threehomework.mappingextend { /// <summary> /// 生成表达式目录树 泛型缓存 /// </summary> /// <typeparam name="tin"></typeparam> /// <typeparam name="tout"></typeparam> public class expressiongenericmapper<tin, tout>//mapper`2 { private static func<tin, tout> _func = null; static expressiongenericmapper() { parameterexpression parameterexpression = expression.parameter(typeof(tin), "p"); list<memberbinding> memberbindinglist = new list<memberbinding>(); foreach (var item in typeof(tout).getproperties()) { memberexpression property = expression.property(parameterexpression, typeof(tin).getproperty(item.name)); memberbinding memberbinding = expression.bind(item, property); memberbindinglist.add(memberbinding); } foreach (var item in typeof(tout).getfields()) { memberexpression property = expression.field(parameterexpression, typeof(tin).getfield(item.name)); memberbinding memberbinding = expression.bind(item, property); memberbindinglist.add(memberbinding); } memberinitexpression memberinitexpression = expression.memberinit(expression.new(typeof(tout)), memberbindinglist.toarray()); expression<func<tin, tout>> lambda = expression.lambda<func<tin, tout>>(memberinitexpression, new parameterexpression[] { parameterexpression }); _func = lambda.compile();//拼装是一次性的 } public static tout trans(tin t) { return _func(t); } } }
总结
以上所述是小编给大家介绍的c#简单实现表达式目录树(expression),希望对大家有所帮助