09-01 C#表达式目录树

目录

一、定义

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

二、与委托的区别

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类型扩展方法的参数是表达式目录树(数据结构)

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);
}

09-01 C#表达式目录树
func本质就是一个方法
09-01 C#表达式目录树
expression本质就是一个数据结构
09-01 C#表达式目录树

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

09-01 C#表达式目录树

三、表达式目录树的本质

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

(1)表达式 2*m+n可以进行拆解,可以分为左边和右边,左边为2*m,右边为n(每一步拆解都是先把右边拆成最小单元)
09-01 C#表达式目录树
(2)左边的2*m还能拆分为左边和右边,左边为2,右边为m09-01 C#表达式目录树
因此,表达式目录树本质就是二叉树
09-01 C#表达式目录树

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

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

1、常量表达式目录树

//常量表达式目录树
Expression<Func<int>> exConst = () => 123 + 234;

那么不用快捷方式应该怎么拼接呢?以常量表达式目录树为例:先看下编译后的结果
09-01 C#表达式目录树
那么终极目标就是拼接成上面的结果就可以了,因为是常量相加,所以编译时已经做了优化,直接算出结果

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();

09-01 C#表达式目录树

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

//快捷声明
 Expression<Func<int, int, int>> exp = (m, n) => m * n + m + n + 2;

同样看一下反编译结果,注意要将反编译工具的C#版本选择到1.0,或者2.0才能看到反编译结果
09-01 C#表达式目录树
拆分拼接(从右往左拆)

//参数
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);

拆分过程图
09-01 C#表达式目录树
对应关系
09-01 C#表达式目录树

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

//复杂表达式目录树:参数含有对象
Expression<Func<Person, bool>> lambdaExp = x => x.Id == 1;

先反编译看结果:
09-01 C#表达式目录树
拆分拼接(从右往左拆)

//拆分
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”
09-01 C#表达式目录树
(2) 获取id属性和x.Id09-01 C#表达式目录树
(3) 获取int类型的ToString()方法
09-01 C#表达式目录树
(4) 获取Equals方法
09-01 C#表达式目录树

五、表达式目录树的应用

需求:有两个对象Person和People,字段属性类型都相同,需要将Person转换成People
思路:大致有五种方法

  1. 硬编码:直接给每个属性赋值 (性能最高)
  2. 反射
  3. 序列化和反序列化 (性能最差)
  4. 表达式目录树字典缓存
  5. 表达式目录树泛型缓存(性能与硬编码接近)

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、表达式目录树泛型缓存

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.1、System.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.2、Newtonsoft.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");
    }
}

执行结果表明:性能排名依次为:硬编码–>泛型缓存–>字典缓存–>反射–>序列化反序列化
09-01 C#表达式目录树
本文完整代码:
表达式目录树

上一篇:typeof 、instanceof、Object.prototype.toString.call([])、String[Symbol.hasInstance](str)判断数组的区别?


下一篇:Linux多运营商线路网卡配置