Expression

SunnyFan大约 8 分钟约 2294 字

Expression

表达式目录树的本质、特点、和委托的区别

Expression<Func<People, bool>> expression = p => p.Id == 10;//可以使用lambda表达式声明
Func<People, bool> func = expression.Compile();
bool bResult = func.Invoke(new People()
{
    Id = 10,
    Name = "Richard"
});
Func<int, int, int> func = (m, n) =>
{
    int i = 0;
    return m * n + 2;
};

Expression<Func<int, int, int>> exp = (m, n) => m * n + 2; //快捷声明--使用lambad表达式来声明
var erpPlu = exp.Compile();//表达式目录树可以通过compile 转换成一个委托

动态拼装Expression

{
    //表达式目录树的拼装---最基础版本
    Expression<Func<int>> expression = () => 123 + 234; //没有参数,返回int
    ConstantExpression expression1 = Expression.Constant(123); //常量表达式
    ConstantExpression expression2 = Expression.Constant(234); //常量表达式 
    //二元表达式
    BinaryExpression binaryExpression = Expression.Add(expression1, expression2);
    Expression<Func<int>> expressionReslut = Expression.Lambda<Func<int>>(binaryExpression);
    Func<int> func = expressionReslut.Compile();
    int iResult = func.Invoke();
}
{
    //表达式目录树的拼装---带参数版本
    Expression<Func<int, int>> expression1 = m => m + 1;
    Func<int, int> func = expression1.Compile();
    int iResult = func.Invoke(5);
    ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m");
    ConstantExpression constant = Expression.Constant(1, typeof(int));
    BinaryExpression addExpression = Expression.Add(parameterExpression, constant);
    Expression<Func<int, int>> expression = Expression.Lambda<Func<int, int>>(addExpression, new ParameterExpression[1]
    {
          parameterExpression
    });
    Func<int, int> func1 = expression.Compile();
    int iResult1 = func1.Invoke(5);
}
{
    //表达式目录树的拼装---带有多个参数的
    Expression<Func<int, int, int>> expression = (m, n) => m * n + 2;
    Func<int, int, int> func = expression.Compile();
    int iResult = func.Invoke(10, 20);
    ParameterExpression parameterExpressionM = Expression.Parameter(typeof(int), "m");
    ParameterExpression parameterExpressionN = Expression.Parameter(typeof(int), "n");
    BinaryExpression multiply = Expression.Multiply(parameterExpressionM, parameterExpressionN);
    ConstantExpression constantExpression = Expression.Constant(2);
    BinaryExpression plus = Expression.Add(multiply, constantExpression);
    Expression<Func<int, int, int>> expression1 = Expression.Lambda<Func<int, int, int>>(plus, new ParameterExpression[2]
    {
          parameterExpressionM,
          parameterExpressionN
    });
    Func<int, int, int> func1 = expression1.Compile();
    int iResult1 = func1.Invoke(10, 20);
}
{
    {
        IQueryable<People> peoplesQueyrabable = new List<People>().AsQueryable();
        Expression<Func<People, bool>> predicate = c => c.Id == 10;
        var query = peoplesQueyrabable.Where(predicate); //Expression<Func<TSource, bool>> predicate
    }
    //表达式目录树的拼装---高级篇
    //类似于这种比较复杂的:建议大家可以反编译看看
    //1.把这个快捷声明的表达式目录树,复制到一个单独的类,为了方便反编译查看中间语言
    //2.反编译看中间语言
    {
        {
            Expression<Func<People, bool>> predicate = c => c.Id == 10;
            Func<People, bool> func = predicate.Compile();
            bool bResult = func.Invoke(new People()
            {
                Id = 10
            });
            //1.声明一个变量C;
            ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "c");
            //2.c.id,调用c.的属性---people的属性id,先获取属性
            //a.获取属性--反射 
            FieldInfo fieldId = typeof(People).GetField("Id"); //id
            //b.c.Id  通过parameterExpression来获取 调用Id
            MemberExpression idExp = Expression.Field(parameterExpression, fieldId);
            //3.==  Equeals 是个方法 ,是id的方法,id 是int类型,应该获取int 的 ===
            ConstantExpression constant10 = Expression.Constant(10, typeof(int));
            //c.id==10;
            Expression expressionExp = Expression.Equal(idExp, constant10);
            Expression<Func<People, bool>> predicate1 = Expression.Lambda<Func<People, bool>>(expressionExp, new ParameterExpression[1]
            {
                    parameterExpression
            });
            Func<People, bool> func1 = predicate1.Compile();
            bool bResult1 = func1.Invoke(new People()
            {
                Id = 10
            });
        }
        //表达式目录树的拼装---超级篇
        //如果遇到很长的表达式目录树--拼装建议从右往左拼装
        {
            Expression<Func<People, bool>> predicate = c =>
            c.Id.ToString() == "10"
            && c.Name.Equals("Richard")
            && c.Age > 35;
            Func<People, bool> func = predicate.Compile();
            bool bResult = func.Invoke(new People()
            {
                Id = 10,
                Name = "Richard",
                Age = 36
            });
            Console.WriteLine("****************************************************");
            //1.拼装c.Age > 35;
            ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "c");
            ConstantExpression constant35 = Expression.Constant(35);
            //age
            PropertyInfo propAge = typeof(People).GetProperty("Age");
            //c.Age
            var ageExp = Expression.Property(parameterExpression, propAge);
            //c.Age > 35
            var cagExp = Expression.GreaterThan(ageExp, constant35);
            Console.WriteLine("****************************************************");
            //拼装:c.Name.Equals("Richard")
            //字符串Richard
            ConstantExpression constantrichard = Expression.Constant("Richard");
            // Name属性
            PropertyInfo propName = typeof(People).GetProperty("Name");
            //c.Name
            var nameExp = Expression.Property(parameterExpression, propName);
            //获取equals方法
            MethodInfo equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
            //c.Name.Equals("Richard") 
            var NameExp = Expression.Call(nameExp, equals, constantrichard);
            Console.WriteLine("****************************************************");
            //拼装:c.Id.ToString() == "10"
            ConstantExpression constantExpression10 = Expression.Constant("10", typeof(string));
            //id
            FieldInfo fieldId = typeof(People).GetField("Id");
            //c.Id
            var idExp = Expression.Field(parameterExpression, fieldId);
            //获取ToString
            MethodInfo toString = typeof(int).GetMethod("ToString", new Type[0]);
            //c.Id.ToString();  Expression.Call:调用方法
            var toStringExp = Expression.Call(idExp, toString, Array.Empty<Expression>());
            // c.Id.ToString()=="10"
            var EqualExp = Expression.Equal(toStringExp, constantExpression10);
            Console.WriteLine("****************************************************");
            //c.Id.ToString() == "10"&&c.Name.Equals("Richard") 
            var plus = Expression.AndAlso(EqualExp, NameExp);
            //c.Id.ToString() == "10"&&c.Name.Equals("Richard") && c.Age > 35
            var exp = Expression.AndAlso(plus, cagExp);
            Expression<Func<People, bool>> predicate1 = Expression.Lambda<Func<People, bool>>(exp, new ParameterExpression[1]
            {
                 parameterExpression
            });
            Func<People, bool> func1 = predicate1.Compile();
            bool bResult1 = func1.Invoke(new People()
            {
                Id = 10,
                Name = "Richard",
                Age = 36
            });
        }
    }
}

为什么要这样拼

{
    //SELECT* FROM USER WHERE   name like ""  and  age=10; 
    //在之前,数据库查询基本都是拼接Sql语句;
    //以前根据用户输入拼装条件
    string sql = "SELECT * FROM USER WHERE 1=1";
    Console.WriteLine("用户输入个名称,为空就跳过");
    string name = Console.ReadLine();
    if (!string.IsNullOrWhiteSpace(name))
    {
        sql += $" and name like '%{name}%'";
    }
    Console.WriteLine("用户输入个账号,为空就跳过");
    string account = Console.ReadLine();
    if (!string.IsNullOrWhiteSpace(account))
    {
        sql += $" and account like '%{account}%'";
    }
}
//现在主流的是Linq:---通过条件拼装的一个很长的表达式目录树;到数据库执行的时候;数据库能够认识表达式目录树吗?  当然不认识;  数据库只认识Sql语句; 需要把表达式目录树如进行拆解;组装成一个Sql语句的条件
{
    {
        //来自于数据库的数据
        var dbSet = new List<People>().AsQueryable();
        var result = dbSet.Where(p => p.Age == 25 & p.Name.Contains("阳光下的微笑"));
        //Expression<Func<People, bool>> predicate = p => p.Age == 25 & p.Name.Contains("阳光下的微笑"); 
        Expression<Func<People, bool>> exp = null;
        Console.WriteLine("用户输入个名称,为空就跳过");
        string name = Console.ReadLine();
        if (!string.IsNullOrWhiteSpace(name))
        {
            exp = p => p.Name.Contains(name);
        }
        Console.WriteLine("用户输入个最小年纪,为空就跳过");
        string age = Console.ReadLine();
        if (!string.IsNullOrWhiteSpace(age) && int.TryParse(age, out int iAge))
        {
            exp = p => p.Age > iAge;
        }
        //如果name 和age 都不为空呢?
        //遇到这种我们应该怎么做?  就可以把一整条表达式目录树做拼装;
        //1.代码量多:----封装; 
        //如果封装一个类库---可以拼装的过程,给封装装起来;提供几个方法;只需要把满足条件的小结构表达式传入; 
        //不断的调用;拼接的表达式目录树,就越来长? 
        //动态的拼装查询条件! 
    }
}

案例

public static void MapperTest()
{
    //需求.  需要把People PeopleCopy:
    //1.反射
    //2.序列化
    //3.Automapper
    //4.可以直接new一个对象
    //5.硬编码
    {
        //People people = new People()
        //{
        //    Id = 11,
        //    Name = "剑锋",
        //    Age = 31
        //};
        ////PeopleCopy people1= (PeopleCopy)people; //不能转换? 没有继承关系 
        ////a: 方法一
        //PeopleCopy peopleCopy0 = new PeopleCopy()
        //{
        //    Id = people.Id,
        //    Name = people.Name,
        //    Age = people.Age
        //};
        ////b:方法二
        //PeopleCopy peopleCopy1 = ReflectionMapper.Trans<People, PeopleCopy>(people);
        ////c:方法三
        //PeopleCopy peopleCopy2 = SerializeMapper.Trans<People, PeopleCopy>(people);
        ////这样好不好?
        ////以上三种方式不好:第一种方式:性能好,不灵活;不能共用;如果换成其他的类型就不能用了;
        ////                  第二、三种方式;灵活,但是性能不好!
        ////三种,总是有不完美的地儿;
        ////动态拼装转换过程;动态拼装了硬编码;
        ////d:方法四  支持泛型版本:就可以支持多种类型
        //PeopleCopy peopleCopy3 = ExpressionMapper.Trans<People, PeopleCopy>(people);
        ////peopleCopy3 通过普通缓存+字典缓存; 第一次生成的时候,保存一个委托在缓存中,如果第二次来,委托就可以直接从缓存中获取到,直接使用;---直接运行委托;直接运行硬编码;---效率高;
        ////泛型缓存:就是为不同的类型生成副本:为每一组类型的组合,生成一个副本;
        ////e:方法五:性能最高的: 支持泛型版本:就可以支持多种类型
        ////泛型缓存+动态拼装表达式目录树(拼装了执行的动作--编码-拼装了执行逻辑--在代码运行的时候,动态的生成逻辑);动态的生成硬编码;
        //PeopleCopy peopleCopy4 = ExpressionGenericMapper<People, PeopleCopy>.Trans(people);
        //{
        //    //能不能把People变成PeopleCopy的过程封装在一个委托中;
        //    Func<People, PeopleCopy> func = p => new PeopleCopy
        //    {
        //        Id = p.Id,
        //        Name = p.Name,
        //        Age = p.Age
        //    };
        //    PeopleCopy peopleCopynew = func.Invoke(people);
        //    //如果能够把这个委托给缓存起来;根据我们的诉求,缓存一个委托;委托哪儿来?委托其实可以通过表达式目录树Compile一下,就可以得到一个委托;如果拼装一个表达式目录树,再Compile一下,然后缓存起来;就相当于拼装了这一组的转换逻辑; 
        //    //拼装一下后,缓存下来,如果后面再需要使用,就可以直接使用这个委托了;
        //} 
        //动态的生生硬编码:还有什么技术可以实现?
    } 
    {
        People people = new People()
        {
            Id = 11,
            Name = "Richard",
            Age = 31
        };
        long common = 0;
        long generic = 0;
        long cache = 0;
        long reflection = 0;
        long serialize = 0;
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 1_000_000; i++)
            {
                PeopleCopy peopleCopy = new PeopleCopy()
                {
                    Id = people.Id,
                    Name = people.Name,
                    Age = people.Age
                };
            }
            watch.Stop();
            common = watch.ElapsedMilliseconds;
        }
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 1_000_000; i++)
            {
                PeopleCopy peopleCopy = ReflectionMapper.Trans<People, PeopleCopy>(people);
            }
            watch.Stop();
            reflection = watch.ElapsedMilliseconds;
        }
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 1_000_000; i++)
            {
                PeopleCopy peopleCopy = SerializeMapper.Trans<People, PeopleCopy>(people);
            }
            watch.Stop();
            serialize = watch.ElapsedMilliseconds;
        }
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 1_000_000; i++)
            {
                PeopleCopy peopleCopy = ExpressionMapper.Trans<People, PeopleCopy>(people);
            }
            watch.Stop();
            cache = watch.ElapsedMilliseconds;
        }
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 1_000_000; i++)
            {
                PeopleCopy peopleCopy = ExpressionGenericMapper<People, PeopleCopy>.Trans(people);
            }
            watch.Stop();
            generic = watch.ElapsedMilliseconds;
        }
        Console.WriteLine($"common = { common} ms");
        Console.WriteLine($"reflection = { reflection} ms");
        Console.WriteLine($"serialize = { serialize} ms");
        Console.WriteLine($"cache = { cache} ms");
        Console.WriteLine($"generic = { generic} ms"); //性能好,而且扩展性也好===又要马儿跑,又要马儿不吃草。。。
    }
}