Expression表达式目录树
一、初识expression
1、在上一篇我们讲到了(忘记了可以在看看,点赞在看养成习惯),今天要讲的expression也和委托有一点点关系吧(没有直接关系,只是想要大家看看我其他的文章),expression是.net准备为linq to sql准备的,它的命名空间是system.linq.expressions
2、不知道大家有没有用户orm(对象映射实体)的数据访问层框架,使用过的小伙伴我相信对下面的伪代码不会陌生,我们在where中传入的就是expression<func<tsource, bool>> predicate
3、我们进入expression一看究竟,我们可以看到expression<func<tsource, bool>>里面有一些方法(后面会慢慢道来),最终继承lambdaexpression
4、我们继续进入lambdaexpression,我们看到了一些属性(这些就是我们lambda的组成的方法和属性),但是最终还是看到继承了expression
5、继续一鼓作气进入expression,到这里我们看到了最终的基类它里面也有很多方法,要说的话这两天都说不完,我们就简单的介绍一些常用的
二、循序渐进
1、大家可能看了上面还有一点点蒙,不急我们继续,我们看下面的实际操作,我们可以看到我们创建一个expression和一个委托,我们使用compile方法可以将expression转换成委托,最后我们执行的结果是一样的。(大家是不是觉得,expression和一个委托差不多呢?哈哈答案肯定不是)
{ //这里我们看这着和委托差不多,但是它还真不是委托 expression<func<int, int>> expression = x => x + 10; //compile方法可以将expression转换成委托 func<int, int> func = expression.compile(); //直接声明委托 func<int, int> func1 = x => x + 10; console.writeline("转换之后的委托--" + func.invoke(5)); console.writeline("委托--" + func1.invoke(5)); }
2、接下来我们进一步的解析我们直接使用lambda表达式创建expression<func<int, int, int>> expression = (m, n) => m * n + 3; 然后我们在使用底层代码实现这句代码,我们也可以很清楚的看到这里我们一步一步的拆解,里面使用了expression中一些对象创建的
//下面我们使用原始的方式创建一个expression<func<int, int, int>> //创建一个m参数 这里的参数是值的(m,n)的,如果说你有几个参数就创建几个 parameterexpression parameter = expression.parameter(typeof(int), "m"); //创建一个n参数 parameterexpression parameter1 = expression.parameter(typeof(int), "n"); //创建一个常量3 constantexpression constant = expression.constant(3, typeof(int)); //首先算出最左边的m*n的结果 binaryexpression binaryexpression = expression.multiply(parameter, parameter1); //然后算出(m*n)+3的结果 binaryexpression = expression.add(binaryexpression, constant); //将上面分解的步骤拼接成lambda expression<func<int, int, int>> expression1 = expression.lambda<func<int, int, int>>(binaryexpression, new parameterexpression[] { parameter, parameter1 }); console.writeline("lambda表达式方式--" + expression.compile()(5, 6)); console.writeline("自己写的组装" + expression1.compile()(5, 6));
3、如果你觉得,还不够我们就写几个实例expression<func<student, bool>> expression = x => x.id.equals(15);
//首先还是定义一个x参数对于上面的x的参数 parameterexpression parameter = expression.parameter(typeof(student), "x"); //首先我们还是从左边进行拆分 获取到属性 memberexpression property = expression.property(parameter, typeof(student).getproperty("id")); //获取我们的方法 methodinfo equals = typeof(student).getmethod("equals"); //定义我们的常量 constantexpression constant = expression.constant("15", typeof(string)); //定义一个方法拼接、第一个参数是我们的属性,第二个参数是使用的方法,第三个参数是传入方法的参数 methodcallexpression coll = expression.call(property, equals, new expression[] { constant }); //所有的数据解析完了之后,我们就需要将参数、方法进行拼装了 expression<func<student, bool>> expression1 = expression.lambda<func<student, bool>>(coll, new parameterexpression[] { parameter }); student student = new student { id = 15 }; console.writeline("lambda表达式方式--" + expression.compile()(student)); console.writeline("自己组装方式--" + expression1.compile()(student));
4、我们可以看出expression就是进行图下的不断拆解,然后在进行组装lambda执行
三、渐入佳境
1、我记得我之前在写automapper的时候说要给大家写一次,这次我就满足大家,我们在写mode和entity转换的时候,量少的时候我们会直接写硬编码
student student = new student { id = 15, name = "产品粑粑", age = 18 }; //硬编码 { //硬编码转换 studentmodel studentmodel = new studentmodel { id = student.id, name = student.name, age = student.age }; }
2、但是我们项目中使用的次数过于频繁后,我们就会使用automapper自动映射了,今天我们就不使用它了我们决定自己造*,我们分别使用(1,反射的方式、2,表达式目录树+字典、3,表达式目录树+泛型委托)
studentmodel studentmodel = new studentmodel(); type type1 = student.gettype(); type type2 = studentmodel.gettype(); foreach (var item in type2.getproperties()) { //判断是不是存在 if (type1.getproperty(item.name) != null) { item.setvalue(studentmodel, type1.getproperty(item.name).getvalue(student)); } }
/// <summary> /// 词典方法推展 /// </summary> public class dictionariesexpand<t, tout> { /// <summary> /// 创建一个静态的容器存放委托 /// </summary> private static dictionary<string, func<t, tout>> pairs = new dictionary<string, func<t, tout>>(); /// <summary> /// 转换对象 /// </summary> /// <typeparam name="t">输入对象</typeparam> /// <param name="obj">输入参数</param> /// <returns></returns> public static tout toobj(t obj) { //生成 string key = typeof(t).fullname + typeof(tout).fullname; if (!pairs.containskey(key)) { //首先我们还是创建一个参数 parameterexpression parameter = expression.parameter(typeof(t)); //获取要转化后的类型 type type = typeof(tout); //创建一个容器存放解析的成员 list<memberbinding> list = new list<memberbinding>(); //遍历属性 foreach (var item in type.getproperties()) { //获取参数中item.name对应的名称 memberexpression memberexpression = expression.property(parameter, typeof(t).getproperty(item.name)); //判断是否存在 if (memberexpression != null) { memberbinding member = expression.bind(item, memberexpression); list.add(member); } } //遍历字段 foreach (var item in type.getfields()) { //获取参数中item.name对应的名称 memberexpression memberexpression = expression.field(parameter, typeof(t).getfield(item.name)); //判断是否存在 if (memberexpression != null) { memberbinding member = expression.bind(item, memberexpression); list.add(member); } } //初始化转换后的类型,并且进行初始化赋值 memberinitexpression memberinit = expression.memberinit(expression.new(typeof(tout)), list); //所有的准备工作已经完成准备生成lambda expression<func<t, tout>> expression = expression.lambda<func<t, tout>>(memberinit, new parameterexpression[] { parameter }); func<t, tout> entrust = expression.compile(); //生成委托存放到我们的字典 pairs.add(key, entrust); return entrust.invoke(obj); } return pairs[key].invoke(obj); } }
/// <summary> /// 泛型方法推展 /// 当我们使用静态方法,会执行静态的无参构造函数 ,不会调用无参构造函数 /// 我们使用泛型的时候会保存不同泛型的副本,一直保存在内存里面不会释放,所以可以 /// 实现伪硬编码 /// </summary> public class genericityexpand<t, tout> { private static func<t, tout> _func = null; static genericityexpand() { //首先我们还是创建一个参数 parameterexpression parameter = expression.parameter(typeof(t)); //获取要转化后的类型 type type = typeof(tout); //创建一个容器存放解析的成员 list<memberbinding> list = new list<memberbinding>(); //遍历属性 foreach (var item in type.getproperties()) { //获取参数中item.name对应的名称 memberexpression memberexpression = expression.property(parameter, typeof(t).getproperty(item.name)); //判断是否存在 if (memberexpression != null) { memberbinding member = expression.bind(item, memberexpression); list.add(member); } } //遍历字段 foreach (var item in type.getfields()) { //获取参数中item.name对应的名称 memberexpression memberexpression = expression.field(parameter, typeof(t).getfield(item.name)); //判断是否存在 if (memberexpression != null) { memberbinding member = expression.bind(item, memberexpression); list.add(member); } } //初始化转换后的类型,并且进行初始化赋值 memberinitexpression memberinit = expression.memberinit(expression.new(typeof(tout)), list); //所有的准备工作已经完成准备生成lambda expression<func<t, tout>> expression = expression.lambda<func<t, tout>>(memberinit, new parameterexpression[] { parameter }); //生成委托存放到我们的泛型委托中 _func = expression.compile(); } public static tout toobj(t obj) { return _func(obj); } }
3、我针对上面的代码,进行了循环百万次的测试
1. 直接硬编码的形式:速度最快(0.126s)
2. 通过反射遍历属性的形式 (6.328s)
3. 利用序列化和反序列化的形式:将复制实体序列化字符串,在把该字符串反序列化被赋值实体(7.768s)
4. 字典缓存+表达式目录树(lambda的拼接代码了解即可) (2.134s)
5. 泛型缓存+表达式目录树(lambda的拼接代码了解即可) (0.663s)
四、总结
1、还有一些其他的用法我还没有完全介绍,比如可以封装一个自己的orm,我们使用的orm就是通过这个进行封装的,授人以鱼不如授人以渔。在最后的一个实例中我们使用到了很多细节的知识点。
上一篇: 【WPF学习】第五十九章 理解控件模板
下一篇: 搭建私有 Nuget 服务器教程(1)