ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

09-01 C#表达式目录树

2021-02-06 18:00:50  阅读:136  来源: 互联网

标签:01 C# 09 watch typeof 序列化 Expression 目录 表达式


目录

一、定义

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

二、与委托的区别

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

在这里插入图片描述
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);

在这里插入图片描述

三、表达式目录树的本质

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

(1)表达式 2*m+n可以进行拆解,可以分为左边和右边,左边为2*m,右边为n(每一步拆解都是先把右边拆成最小单元)
在这里插入图片描述
(2)左边的2*m还能拆分为左边和右边,左边为2,右边为m在这里插入图片描述
因此,表达式目录树本质就是二叉树
在这里插入图片描述

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

使用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;

同样看一下反编译结果,注意要将反编译工具的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;

先反编译看结果:
在这里插入图片描述
拆分拼接(从右往左拆)

//拆分
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方法
在这里插入图片描述

五、表达式目录树的应用

需求:有两个对象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");
    }
}

执行结果表明:性能排名依次为:硬编码–>泛型缓存–>字典缓存–>反射–>序列化反序列化
在这里插入图片描述
本文完整代码:
表达式目录树

标签:01,C#,09,watch,typeof,序列化,Expression,目录,表达式
来源: https://blog.csdn.net/weixin_39305029/article/details/110295269

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有