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 + ;
//Compile方法可以将Expression转换成委托
Func<int, int> func = expression.Compile();
//直接声明委托
Func<int, int> func1 = x => x + ;
Console.WriteLine("转换之后的委托--" + func.Invoke());
Console.WriteLine("委托--" + func1.Invoke());
}

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(, 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()(, ));
Console.WriteLine("自己写的组装" + expression1.Compile()(, ));
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("", 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 =
};
Console.WriteLine("lambda表达式方式--" + expression.Compile()(student));
Console.WriteLine("自己组装方式--" + expression1.Compile()(student));
4、我们可以看出Expression就是进行图下的不断拆解,然后在进行组装lambda执行

三、渐入佳境
1、我记得我之前在写AutoMapper的时候说要给大家写一次,这次我就满足大家,我们在写Mode和Entity转换的时候,量少的时候我们会直接写硬编码
Student student = new Student
{
ID = ,
Name = "产品粑粑",
Age =
};
//硬编码
{
//硬编码转换
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就是通过这个进行封装的,授人以鱼不如授人以渔。在最后的一个实例中我们使用到了很多细节的知识点。
Expression表达式目录树的更多相关文章
- 第十五节:Expression表达式目录树(与委托的区别、自行拼接、总结几类实例间的拷贝)
一. 基本介绍 回忆: 最早接触到表达式目录树(Expression)可能要追溯到几年前使用EF早期的时候,发现where方法里的参数是Expression<Func<T,bool> ...
- 【学习笔记】Expression表达式目录树
Expression表达式目录树:一个能拼装能解析的数据结构,语法树. 一.手动拼装表达式目录树 示例1: /// <summary> /// 展示表达式树,协助用的 /// 编译lamb ...
- 【手撸一个ORM】第五步、Expression(表达式目录树)转换为Where子句
说明 在SQL中,查询.修改比较常用到WHERE子句,在这里根据使用场景不同,定义了两个类,一个用于查询,一个用于修改(插入)操作.原因是: 查询操作支持一级导航属性查询,如student.Schoo ...
- EXpression 表达式目录树
表达式树 前面n-1的是一个表达式 最后一个是一个表达式 一直拆开拆到最后 继承ExpressionVisitor的类 可以重写获取到表达式树的方法进行扩张和改写 委托是编译成一个方法 表达 ...
- 学习笔记: Expression表达式目录树详解和扩展封装
1. 表达式链接扩展封装,ORM常用 And Or /// <summary> /// 表达式访问者 /// </summary> public class Expressi ...
- Expression表达式目录树动态拼接 反射获取泛型方法
class TestOne { public String[] arr = { "1", "2", "3" }; public class ...
- C#表达式目录树(Expression)
1.什么是表达式目录树 :简单的说是一种语法树,或者说是一种数据结构(Expression) 2.用Lambda声明表达式目录树: Expression<Func<; //表达试目录树的方 ...
- C#简单实现表达式目录树(Expression)
1.什么是表达式目录树 :简单的说是一种语法树,或者说是一种数据结构(Expression) 2.用Lambda声明表达式目录树: 1 2 3 4 5 Expression<Func<in ...
- 第十九节: 结合【表达式目录树】来封装EF的BaseDal层的方法
一. 简介 该章节,可以说是一个简单轻松的章节,只要你对Expression表达式树.EF的基本使用.泛型有所了解,那么本章节实质上就是一个非常简单的封装章节,便于我们快捷开发. PS:在该章节对于E ...
随机推荐
- Angular开发者指南(六)作用域
什么是作用域? 作用域是引用应用程序模型的对象. 它是表达式的执行上下文. 作用域以层次结构排列,模仿应用程序的DOM结构,它可以观察表达式和传播事件. 作用域的特征 Scope提供API($watc ...
- [LC] 215. Kth Largest Element in an Array
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the so ...
- OpenWrt Web 开发 LuCI框架 lua语言
LuCI作为“FFLuCI”诞生于2008年3月份,目的是为OpenWrt固件从 Whiterussian 到 Kamikaze实现快速配置接口.Lua是一个小巧的脚本语言,很容易嵌入其它语言.轻量级 ...
- MySQL性能优化最佳实践 - 05 MySQL核心参数优化
back_log参数的作用 指定MySQL可能的TCP/IP的连接数量(一个TCP/IP连接占256k),默认是50.当MySQL主线程在很短的时间内得到非常多的连接请求,该参数就起作用,之后主线程花 ...
- Java JDBC调用inout类型参数的存储过程
存储过程参数类型:in.out.inout,in:输入类型,out:输出类型,inout:既可输入,也可以输出. 一.JDBC调用inout类型参数的存储过程,并且获得返回值 Class.forNam ...
- 一分钟搞定pychram远程调试和同步代码
首先说一下需求,否则很多人都不知道pycharm这个远程同步和调试到底是干嘛使的. 需求很简单,我想要在本地的windows机器上跑一个程序,但是程序运行会加载一些很占内存的树型数据结构,称其为tre ...
- JavaScript值类型和引用类型有哪些
JavaScript值类型和引用类型有哪些 (1)值类型:数值.布尔值.null.undefined. (2)引用类型:对象.数组.函数.
- Spring Boot框架开发的Java项目在CentOS7上的部署
需求:上级拿来一份Spring Boot框架开发的Java项目代码让我在服务器上运行起来,只说了一句该框架是自带了Tomcat(不用重新安装Tomcat),一份代码下有两个项目(一个管理端项目,一个用 ...
- 4K手机能拯救索尼手机吗?
智能手机屏幕分辨率究竟达到多少才是极限,一直是业内争论不休的问题.从低分辨率一路走来,直到iPhone 4搭载视网膜屏,业内才有了一个较为统一的认知:屏幕起码要在合适距离下看不到文字.图像虚影,才称得 ...
- Mac下好玩的终端命令
figlet brew install figlet cowsay brew install cowsaycowsay -l: 查看所有可用动物cowsay -f daemon hello world ...