表达式树有自身的复杂写的方式,Lambda是表达式树的简写。

极简本质:表达式树是一堆关于二叉计算公式树的类定义,但是最终一个计算公式被编译成了一个方法【委托】。 然后依赖对委托的调用invoke去执行,得到计算结果。

Y=x+1

实质是一个方法,是一个function。  这样看就知道为啥国外的英文的人理解很简单,但是中文理解就很难了。委托完全可以叫做function。后期也确实抽象出了一个公共的func

 

那么lambda是关于【方法函数】 的语法糖。

 

所以表达式树--二叉计算公式树--方法, 是不是可以用lambda来简写。

 

那么既然labda可以简写, 不简写的时候是怎么定义的?一堆表达式类、操作符类、最后转成委托。

 

表达式目录树是一种语法树,是一种数据结构一种数据结构------->二叉树

扩展方法的表达式分两种:

 

IEnumerable类型【迭代器】的扩展方法的参数是委托

IQueryable类型扩展方法的参数是表达式目录树(数据结构)

 

定义--一行、没有大括号

执行--先调用Compile()转换成委托invoke--------------最终都要编译成1个方法【委托】,才能invoke被执行。

不简写--原生的表达式树是怎么定义的?

 

既然labda可以简写, 不简写的时候是怎么定义的?一堆表达式类、操作符类、最后转成委托。

那么lambda简写,又是怎么简写的?

 

 

 

 

表达式树

 

Lambda表达式和表达式树 - 田小计划 - 博客园 (cnblogs.com)

 

一、定义

二、与委托的区别

1、在扩展方法表达式中的区别

2、声明方式的区别

3、执行的区别

三、表达式目录树的本质

四、表达式目录树的拆分/拼接

1、常量表达式目录树

2、复杂的表达式目录树(简单参数)

3、复杂表达式目录树(复杂参数)

五、表达式目录树的应用

1、硬编码

2、反射

3、序列化和反序列化

4、表达式目录树字典缓存

5、表达式目录树泛型缓存

 

 

1、定义-表达式目录树是一种语法树,是一种数据结构

表达式目录树是一种语法树,是一种数据结构

 

2、与委托的区别

1、在扩展方法表达式中的区别

List<Person> persons = new Person().Query();

persons.Where(p => p.Id == 1);   // where扩展方法传入的是一个委托

persons.AsQueryable().Where(p => p.Id == 1); //where 扩展方法传入的是一个Expression(表达式目录树)

 IEnumerable类型的扩展方法的参数是委托

IQueryable类型扩展方法的参数是表达式目录树(数据结构)

 

注意:上面看上去不论是委托,还是表达式树,都被lamada简写了。

 

2、声明方式的区别

委托的方法体可以有多行

表达式目录树的方法体只能有一行,且不能用大括号

public static void Show()

{

    Func<int, int, int> func = (m, n) => 2 * m + n;

 

    Func<int, int, int> func1 = (m, n) =>

    {

        Console.WriteLine("委托的方法体可以有多行");

        return 2 * m + n;

    };

    Expression<Func<int, int, int>> expression = (m, n) => 2 * m + n;

 

    //Expression<Func<int, int, int>> expression1 = (m, n) =>

    //{

    //    Console.WriteLine("表达式目录树的方法体只能有一行,且不能用大括号");

    //    return 2 * m + n;

    //}

 

    int funcResult = func.Invoke(1, 2);

    Console.WriteLine(funcResult);

    int expResult = expression.Compile().Invoke(1, 2);

    Console.WriteLine(expResult);

}

 

func本质就是一个方法

 

expression本质就是一个数据结构

 

 

3、执行的区别

执行委托直接Invoke

执行表达式目录树:expression.Compile().Invoke(1, 2);先调用Compile()转换成委托,在执行Invoke

int funcResult = func.Invoke(1, 2);

int funcResult1 = func(1, 2);

Console.WriteLine(funcResult);

int expResult = expression.Compile().Invoke(1, 2);

int expResult1 = expression.Compile()(1, 2);

Console.WriteLine(expResult);

 

 

 

 

3、表达式目录树的本质

表达式目录树的本质是一种数据结构------->二叉树

1

1)表达式 2*m+n可以进行拆解,可以分为左边和右边,左边为2*m,右边为n(每一步拆解都是先把右边拆成最小单元)

 

2)左边的2*m还能拆分为左边和右边,左边为2,右边为m

因此,表达式目录树本质就是二叉树

 

 

4、表达式目录树的拆分/拼接

使用lambda的方式属于快捷声明方式

 

1、常量表达式目录树

//常量表达式目录树

Expression<Func<int>> exConst = () => 123 + 234;

 

 

那么不用快捷方式应该怎么拼接呢?以常量表达式目录树为例:先看下编译后的结果

 

那么终极目标就是拼接成上面的结果就可以了,因为是常量相加,所以编译时已经做了优化,直接算出结果

 

Expression expLift = Expression.Constant(123); //左边

Expression expRight = Expression.Constant(234); //右边

Expression expSum = Expression.Add(expLift, expRight);

Expression<Func<int>> expFun =  Expression.Lambda<Func<int>>(expSum);//做成委托

int result = expFun.Compile().Invoke();

 

 

2、复杂的表达式目录树(简单参数)

//快捷声明

 Expression<Func<int, int, int>> exp = (m, n) => m * n + m + n + 2;

1

2

同样看一下反编译结果,注意要将反编译工具的C#版本选择到1.0,或者2.0才能看到反编译结果

 

拆分拼接(从右往左拆)

 

//参数

ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m");

ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "n");

//常量2

Expression expConst = Expression.Constant(2, typeof(int));

//m*n

Expression expMultiply = Expression.Multiply(parameterExpression, parameterExpression2);

//m*n+m

Expression expSum = Expression.Add(expMultiply, parameterExpression);

// m*n+m+n

Expression expSum1 = Expression.Add(expSum, parameterExpression2);

//m*n+m+n+2

Expression expSum2 = Expression.Add(expSum1, expConst);

//转换成lambda

Expression<Func<int, int, int>> exp1 = Expression.Lambda<Func<int, int, int>>(expSum2, new ParameterExpression[2]

{

    parameterExpression,

    parameterExpression2

});

int iResult = exp1.Compile().Invoke(10, 11);

 

 

拆分过程图

 

对应关系

 

 

3、复杂表达式目录树(复杂参数)

//复杂表达式目录树:参数含有对象

Expression<Func<Person, bool>> lambdaExp = x => x.Id == 1;

1

2

先反编译看结果:

 

拆分拼接(从右往左拆)

 

//拆分

ParameterExpression parameterExpression = Expression.Parameter(typeof(Person), "x");

//常量"1"

Expression expConst = Expression.Constant("1", typeof(string));

//获取Id属性

PropertyInfo idProp = typeof(Person).GetProperty("Id");

//获取到x.Id

Expression expId = Expression.Property(parameterExpression, idProp);

//获取int类型的ToString方法(获取到无参的方法)

MethodInfo toString = typeof(int).GetMethod("ToString", new Type[0]);

//获取x.Id.ToString()

Expression expToString = Expression.Call(expId, toString, Array.Empty<Expression>());

//获取到Equals方法

MethodInfo equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });

//获取x.Id.ToString()Equals("1")

Expression expEquals = Expression.Call(expToString, equals, expConst);

 

//最终替换结果

Expression<Func<Person, bool>> lambdaExp1 = Expression.Lambda<Func<Person, bool>>(expEquals, new ParameterExpression[1]

{

    parameterExpression

});

 

Func<Person, bool> func = lambdaExp1.Compile();

bool result = func.Invoke(new Person { Id = 1, Name = "张三" });

 

 

拆解过程

1) 获取常量“1”

 

2) 获取id属性和x.Id

3) 获取int类型的ToString()方法

 

4) 获取Equals方法

 

 

 

5、表达式目录树的应用

需求:有两个对象PersonPeople,字段属性类型都相同,需要将Person转换成People

思路:大致有五种方法

 

硬编码:直接给每个属性赋值 (性能最高)

反射

序列化和反序列化 (性能最差)

表达式目录树字典缓存

表达式目录树泛型缓存(性能与硬编码接近)

1、硬编码

public People(Person person)

{

    Id = person.Id;

    Name = person.Name;

}

 

2、反射

class ReflectionMapper

{

    /// <summary>

    /// 反射转换对象

    /// </summary>

    /// <typeparam name="TSource"></typeparam>

    /// <typeparam name="TTarget">目标</typeparam>

    /// <param name="source"></param>

    /// <returns></returns>

    public static TTarget Mapper<TSource, TTarget>(TSource source)

    {

        TTarget target = Activator.CreateInstance<TTarget>();

        PropertyInfo[] targetPropertyInfos = target.GetType().GetProperties();

        Type sourceType = source.GetType();

        foreach (var targetProp in targetPropertyInfos)

        {

            PropertyInfo sourceProp = sourceType.GetProperty(targetProp.Name);

            targetProp.SetValue(target, sourceProp.GetValue(source));

        }

        FieldInfo[] targetFieldInfos = target.GetType().GetFields();

        foreach (var targetField in targetFieldInfos)

        {

            FieldInfo sourceField = sourceType.GetField(targetField.Name);

            targetField.SetValue(target, sourceField.GetValue(source));

        }

        return target;

    }

}

 

3序列化和反序列化

System.Text.Json序列化和反序列化转换对象(性能略优)

Newtonsoft.Json序列化和反序列化转换对象

 class SerializeMapper

 {

     /// <summary>

     /// System.Text.Json序列化和反序列化转换对象

     /// </summary>

     /// <typeparam name="TSource"></typeparam>

     /// <typeparam name="TTarget"></typeparam>

     /// <param name="source"></param>

     /// <returns></returns>

     public static TTarget Mapper<TSource, TTarget>(TSource source)

     {

         return System.Text.Json.JsonSerializer.Deserialize<TTarget>(System.Text.Json.JsonSerializer.Serialize(source));

     }

 

     /// <summary>

     /// Newtonsoft.Json序列化和反序列化转换对象

     /// </summary>

     /// <typeparam name="TSource"></typeparam>

     /// <typeparam name="TTarget"></typeparam>

     /// <param name="source"></param>

     /// <returns></returns>

     public static TTarget NewtonMapper<TSource, TTarget>(TSource source)

     {

         return JsonConvert.DeserializeObject<TTarget>(JsonConvert.SerializeObject(source));

     }

 }

 

4、表达式目录树字典缓存

class ExpressionDicMapper

{

    /// <summary>

    /// 字典缓存--hash分布

    /// </summary>

    private static Dictionary<string, object> _Dic = new Dictionary<string, object>();

 

    /// <summary>

    /// 字典缓存目录树

    /// </summary>

    /// <typeparam name="TSource"></typeparam>

    /// <typeparam name="TTarget"></typeparam>

    /// <param name="source"></param>

    /// <returns></returns>

    public static TTarget Mapper<TSource, TTarget>(TSource source)

    {

        string key = $"funckey_{typeof(TSource).FullName}_{typeof(TTarget).FullName}";

        if (!_Dic.ContainsKey(key))

        {

            ParameterExpression parameterExpression = Expression.Parameter(typeof(TSource), "p");

            List<MemberBinding> memberBindingList = new List<MemberBinding>();

            PropertyInfo[] targetPropertyInfos = typeof(TTarget).GetProperties();

            foreach (var targetProp in targetPropertyInfos)

            {

                //获取属性

                Expression property = Expression.Property(parameterExpression, typeof(TSource).GetProperty(targetProp.Name));

                MemberBinding memberBinding = Expression.Bind(targetProp, property);

                memberBindingList.Add(memberBinding);

            }

            FieldInfo[] targetFieldInfos = typeof(TTarget).GetFields();

            foreach (var targetField in targetFieldInfos)

            {

                //获取属性

                Expression field = Expression.Field(parameterExpression, typeof(TSource).GetField(targetField.Name));

                MemberBinding memberBinding = Expression.Bind(targetField, field);

                memberBindingList.Add(memberBinding);

            }

 

            MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TTarget)), memberBindingList.ToArray());

            Expression<Func<TSource, TTarget>> exp = Expression.Lambda<Func<TSource, TTarget>>(memberInitExpression, new ParameterExpression[1]

            {

                parameterExpression

            });

            //拼装是一次性的。如果字典中包含了,就不会再拼装了,实现了缓存

            Func<TSource, TTarget> func = exp.Compile();

            _Dic[key] = func;

        }

        return ((Func<TSource, TTarget>)_Dic[key]).Invoke(source);

    }

}

 

5、表达式目录树泛型缓存----------将这个转换提出成了方法--委托,参数是泛型T

class ExpressionGenericMapper<TSource, TTarget>

{

    /// <summary>

    /// 泛型委托缓存

    /// </summary>

    private static Func<TSource, TTarget> _Func = null;

 

    /// <summary>

    /// 静态化构造函数实现仅拼装一次表达式目录树

    /// </summary>

    static ExpressionGenericMapper()

    {

        ParameterExpression parameterExpression = Expression.Parameter(typeof(TSource), "p");

        List<MemberBinding> memberBindingList = new List<MemberBinding>();

        PropertyInfo[] targetPropertyInfos = typeof(TTarget).GetProperties();

        foreach (var targetProp in targetPropertyInfos)

        {

            //获取属性

            Expression property = Expression.Property(parameterExpression, typeof(TSource).GetProperty(targetProp.Name));

            MemberBinding memberBinding = Expression.Bind(targetProp, property);

            memberBindingList.Add(memberBinding);

        }

        FieldInfo[] targetFieldInfos = typeof(TTarget).GetFields();

        foreach (var targetField in targetFieldInfos)

        {

            //获取属性

            Expression field = Expression.Field(parameterExpression, typeof(TSource).GetField(targetField.Name));

            MemberBinding memberBinding = Expression.Bind(targetField, field);

            memberBindingList.Add(memberBinding);

        }

 

        MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TTarget)), memberBindingList.ToArray());

        Expression<Func<TSource, TTarget>> exp = Expression.Lambda<Func<TSource, TTarget>>(memberInitExpression, new ParameterExpression[1]

        {

                parameterExpression

        });

        //拼装是一次性的,如果泛型委托中包含了,就不会再拼装了,实现了缓存

        _Func = exp.Compile();

    }

 

    /// <summary>

    /// 对外方法

    /// </summary>

    /// <param name="source"></param>

    /// <returns></returns>

    public static TTarget Mapper(TSource source)

    {

        return _Func.Invoke(source);

    }

}

 

测试代码

 

public static void ExpressMapper()

{

    Person person = new Person

    {

        Id = 1,

        Name = "张三"

    };

    {

        /*

         * 1、硬编码:直接给每个属性赋值

         */

        Stopwatch watch = new Stopwatch();

        watch.Start();

        //循环一百万次

        for (int i = 0; i < 1_000_000; i++)

        {

            People people = new People(person);

        }

        watch.Stop();

        Console.WriteLine($"硬编码耗时:{watch.ElapsedMilliseconds} ms");

    }

 

    {

        /*

         * 2、反射

         */

        Stopwatch watch = new Stopwatch();

        watch.Start();

        //循环一百万次

        for (int i = 0; i < 1_000_000; i++)

        {

            People people = ReflectionMapper.Mapper<Person, People>(person);

        }

        watch.Stop();

        Console.WriteLine($"反射耗时:{watch.ElapsedMilliseconds} ms");

    }

 

    {

        /*

         * 3.1System.Text.Json序列化和反序列化转换对象

         */

        Stopwatch watch = new Stopwatch();

        watch.Start();

        //循环一百万次

        for (int i = 0; i < 1_000_000; i++)

        {

            People people = SerializeMapper.Mapper<Person, People>(person);

        }

        watch.Stop();

        Console.WriteLine($"System.Text.Json反序列化和反序列化耗时:{watch.ElapsedMilliseconds} ms");

    }

 

    {

        /*

         * 3.2Newtonsoft.Json序列化和反序列化转换对象

         */

        Stopwatch watch = new Stopwatch();

        watch.Start();

        //循环一百万次

        for (int i = 0; i < 1_000_000; i++)

        {

            People people = SerializeMapper.NewtonMapper<Person, People>(person);

        }

        watch.Stop();

        Console.WriteLine($"Newtonsoft.Json反序列化和反序列化耗时:{watch.ElapsedMilliseconds} ms");

    }

 

    {

        /*

         * 4、表达式目录树字典缓存

         */

        Stopwatch watch = new Stopwatch();

        watch.Start();

        //循环一百万次

        for (int i = 0; i < 1_000_000; i++)

        {

            People people = ExpressionDicMapper.Mapper<Person, People>(person);

        }

        watch.Stop();

        Console.WriteLine($"表达式目录树字典缓存耗时:{watch.ElapsedMilliseconds} ms");

    }

 

    {

        /*

         * 5、表达式目录树泛型缓存

         */

        Stopwatch watch = new Stopwatch();

        watch.Start();

        //循环一百万次

        for (int i = 0; i < 1_000_000; i++)

        {

            People people = ExpressionGenericMapper<Person, People>.Mapper(person);

        }

        watch.Stop();

        Console.WriteLine($"表达式目录树泛型缓存耗时:{watch.ElapsedMilliseconds} ms");

    }

}

 

执行结果表明:性能排名依次为:硬编码–>泛型缓存–>字典缓存–>反射–>序列化反序列化

 

5种和第4种,区别在哪里?

1. 区别是调用方法, 调用静态方法和调用委托。

2. 静态方法,现去执行表达式,生成一个委托。

   泛型方法,也是现去执行表达式,生成1个委托。

3. 静态方法是每次都从字典中获取

   泛型方法,是构造函数定义。

   可能在构造函数时,就已经初始化了这个func委托。 节省了委托生成的时间。

4. 静态方法 和泛型方法,内部的表达式定义都是相同的。

 

问题可能主要出在,泛型类的静态构造函数+静态func字段,会被编译器提前生成?

执行顺序:

子类静态变量初始化>子类静态构造函数>

父类静态变量初始化 > 父类静态构造函数 > 父类实例变量初始化>父类实例构造函数

 > 子类实例变量初始化>本身实例构造函数。

   

 

 

 

 

使用泛型类的静态构造函数--来提前生成【方法--委托】--执行效率更高