一、初识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表达式目录树的更多相关文章

  1. 第十五节:Expression表达式目录树(与委托的区别、自行拼接、总结几类实例间的拷贝)

    一. 基本介绍 回忆: 最早接触到表达式目录树(Expression)可能要追溯到几年前使用EF早期的时候,发现where方法里的参数是Expression<Func<T,bool> ...

  2. 【学习笔记】Expression表达式目录树

    Expression表达式目录树:一个能拼装能解析的数据结构,语法树. 一.手动拼装表达式目录树 示例1: /// <summary> /// 展示表达式树,协助用的 /// 编译lamb ...

  3. 【手撸一个ORM】第五步、Expression(表达式目录树)转换为Where子句

    说明 在SQL中,查询.修改比较常用到WHERE子句,在这里根据使用场景不同,定义了两个类,一个用于查询,一个用于修改(插入)操作.原因是: 查询操作支持一级导航属性查询,如student.Schoo ...

  4. EXpression 表达式目录树

    表达式树   前面n-1的是一个表达式  最后一个是一个表达式  一直拆开拆到最后 继承ExpressionVisitor的类  可以重写获取到表达式树的方法进行扩张和改写 委托是编译成一个方法 表达 ...

  5. 学习笔记: Expression表达式目录树详解和扩展封装

    1. 表达式链接扩展封装,ORM常用 And  Or /// <summary> /// 表达式访问者 /// </summary> public class Expressi ...

  6. Expression表达式目录树动态拼接 反射获取泛型方法

    class TestOne { public String[] arr = { "1", "2", "3" }; public class ...

  7. C#表达式目录树(Expression)

    1.什么是表达式目录树 :简单的说是一种语法树,或者说是一种数据结构(Expression) 2.用Lambda声明表达式目录树: Expression<Func<; //表达试目录树的方 ...

  8. C#简单实现表达式目录树(Expression)

    1.什么是表达式目录树 :简单的说是一种语法树,或者说是一种数据结构(Expression) 2.用Lambda声明表达式目录树: 1 2 3 4 5 Expression<Func<in ...

  9. 第十九节: 结合【表达式目录树】来封装EF的BaseDal层的方法

    一. 简介 该章节,可以说是一个简单轻松的章节,只要你对Expression表达式树.EF的基本使用.泛型有所了解,那么本章节实质上就是一个非常简单的封装章节,便于我们快捷开发. PS:在该章节对于E ...

随机推荐

  1. Angular开发者指南(六)作用域

    什么是作用域? 作用域是引用应用程序模型的对象. 它是表达式的执行上下文. 作用域以层次结构排列,模仿应用程序的DOM结构,它可以观察表达式和传播事件. 作用域的特征 Scope提供API($watc ...

  2. [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 ...

  3. OpenWrt Web 开发 LuCI框架 lua语言

    LuCI作为“FFLuCI”诞生于2008年3月份,目的是为OpenWrt固件从 Whiterussian 到 Kamikaze实现快速配置接口.Lua是一个小巧的脚本语言,很容易嵌入其它语言.轻量级 ...

  4. MySQL性能优化最佳实践 - 05 MySQL核心参数优化

    back_log参数的作用 指定MySQL可能的TCP/IP的连接数量(一个TCP/IP连接占256k),默认是50.当MySQL主线程在很短的时间内得到非常多的连接请求,该参数就起作用,之后主线程花 ...

  5. Java JDBC调用inout类型参数的存储过程

    存储过程参数类型:in.out.inout,in:输入类型,out:输出类型,inout:既可输入,也可以输出. 一.JDBC调用inout类型参数的存储过程,并且获得返回值 Class.forNam ...

  6. 一分钟搞定pychram远程调试和同步代码

    首先说一下需求,否则很多人都不知道pycharm这个远程同步和调试到底是干嘛使的. 需求很简单,我想要在本地的windows机器上跑一个程序,但是程序运行会加载一些很占内存的树型数据结构,称其为tre ...

  7. JavaScript值类型和引用类型有哪些

    JavaScript值类型和引用类型有哪些 (1)值类型:数值.布尔值.null.undefined. (2)引用类型:对象.数组.函数.

  8. Spring Boot框架开发的Java项目在CentOS7上的部署

    需求:上级拿来一份Spring Boot框架开发的Java项目代码让我在服务器上运行起来,只说了一句该框架是自带了Tomcat(不用重新安装Tomcat),一份代码下有两个项目(一个管理端项目,一个用 ...

  9. 4K手机能拯救索尼手机吗?

    智能手机屏幕分辨率究竟达到多少才是极限,一直是业内争论不休的问题.从低分辨率一路走来,直到iPhone 4搭载视网膜屏,业内才有了一个较为统一的认知:屏幕起码要在合适距离下看不到文字.图像虚影,才称得 ...

  10. Mac下好玩的终端命令

    figlet brew install figlet cowsay brew install cowsaycowsay -l: 查看所有可用动物cowsay -f daemon hello world ...