zoukankan      html  css  js  c++  java
  • 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));
                }
    View Code

     

       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));
    View Code

            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));
    View Code

            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
                        };
                    }
    View Code

         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));
                            }
                        }
    View Code
        /// <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);
            }
        }
    View Code
    /// <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);
            }
        }
    View Code

           3、我针对上面的代码,进行了循环百万次的测试

             1. 直接硬编码的形式:速度最快(0.126s)
        2. 通过反射遍历属性的形式 (6.328s)
        3. 利用序列化和反序列化的形式:将复制实体序列化字符串,在把该字符串反序列化被赋值实体(7.768s)
          4. 字典缓存+表达式目录树(Lambda的拼接代码了解即可) (2.134s)
             5. 泛型缓存+表达式目录树(Lambda的拼接代码了解即可) (0.663s)

    四、总结

            1、还有一些其他的用法我还没有完全介绍,比如可以封装一个自己的ORM,我们使用的ORM就是通过这个进行封装的,授人以鱼不如授人以渔。在最后的一个实例中我们使用到了很多细节的知识点。

  • 相关阅读:
    利用 StartLoadingStatus 和 FinishLoadingStatus 读取数据特别是大数据时增加渐隐渐显等待特效
    在Indicator中添加动态Checkbox,无需绑定数据源,支持全选
    修复DBGrideh使用TMemTableEh在Footers求平均值为0的Bug
    字符串操作之格式化
    关于C#里面SQLite读取数据的操作
    多线程“尚未调用coinitialize” 报错
    自动化脚本运行稳定性(一)——脚本健壮性
    接口测试用例编写规范
    测试计划对应用质量的影响
    MySQL数据操作语句精解
  • 原文地址:https://www.cnblogs.com/chenxi001/p/12509741.html
Copyright © 2011-2022 走看看