Expression 表达式目录树二

话题:两个类属性字段一样怎么转化:将Student 转化为 StudentCopy

这里先提供两个方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Task1
{
   public class SerializeMapper
    {
        public static TOut Trans<TIn, TOut> (TIn t )
        {
           return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(t));
        }

    }
}

     public static TOut Trans<TIn, TOut> (TIn t )
        {
            TOut tout = Activator.CreateInstance<TOut>();
            foreach (var prop in typeof(TOut).GetProperties())
            {
                var propinfo = typeof(TIn).GetProperty(prop.Name);
                if (propinfo != null)
                {
                    prop.SetValue(tout, propinfo.GetValue(t));
                }
            }

            foreach (var field in typeof(TOut).GetFields())
            {
                var fieldfo = typeof(TIn).GetProperty(field.Name);
                if (fieldfo != null)
                {
                    field.SetValue(tout, fieldfo.GetValue(t));
                }
            }
            return tout;
        }

以上两个都可以实现,但是以上两种方法 一个是通过反射找属性和字段, 一个通过反射转字符串 都是性能不友好的

有没有即通用又性能良好的方式呢:

思路:利用表达式目录树来实现,其实反编译工具可以将下面的快捷方式反编译为 普通的目录树声明方式,直接抄下来改一下就好


       Expression< Func<Student, StudentCopy>> trans = s => new StudentCopy{ Id = s.Id, Name = s.Name,Age = s.Age};

       StudentCopy studentCopy = trans.Compile()(new Student { Id = 1, Name = "张三", Age = 20 });

需要明白这种方式和如下代码一个意思:

 var student = new Student { Id = 1, Name = "张三", Age = 20 };
 var studentCopy = new StudentCopy { Id = student.Id, Name = student.Name, Age = student.Age };

这里我们实现一下:

  public class ExpressionMapper
    {
        private static Dictionary<string, object> Dic = new Dictionary<string, object>();

        public static TOut Trans<TIn, TOut> (TIn t )
        {
            //模仿对象
            //Expression< Func<Student, StudentCopy>> trans = s => new StudentCopy{ Id = s.Id, Name = s.Name,Age = s.Age};

            string key = string.Format("funkey_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName);
            if (!Dic.ContainsKey(key))
            {
                ParameterExpression s = Expression.Parameter(typeof(TIn), "s");
                List<MemberBinding> memberBindingList = new List<MemberBinding>();
                foreach (var item in typeof(TOut).GetProperties())
                {
                    MemberExpression propExp = Expression.Property(s, typeof(TIn).GetProperty(item.Name));//得到 原类 中的属性 即 s.Id, s.Name,s.Age
                    MemberBinding memberBinding = Expression.Bind(item, propExp);// 将原类的属性绑定到 目标类属性上  即 Id = s.Id, Name = s.Name,Age = s.Age
                    memberBindingList.Add(memberBinding); 
                }
                foreach (var item in typeof(TOut).GetFields())
                {
                    MemberExpression field = Expression.Property(s, typeof(TIn).GetProperty(item.Name));
                    MemberBinding memberBinding = Expression.Bind(item, field);
                    memberBindingList.Add(memberBinding);
                }

                MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());//创建一个Body
                Expression<Func<TIn, TOut>> tempExp = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, s);//创建表达式目录树
                var lamada = tempExp.Compile();//获取委托实例
                Dic.Add(key, lamada);//加入静态字典
            }
            return ((Func<TIn, TOut>)Dic[key])(t);
        }

    }

三种方法都可以实现目的,但是使用目录树的方式是性能最好的

虽然第一次难免需要使用反射查找属性和字段,但是有静态Dictionary作为缓存

性能好且通用

 和直接写代码复制没有区别,但是实现代码相对复杂一点,可以看见都可以顺利完成

这里还可以结合以前学习的 静态字典 因为  Dictionary 类  和 静态字典的效率是没发比的  如下:静态字典出来的每个类都是独立的不用像

Dictionary 一样还要去查找 这里的查找会损耗大量性能

 public class ExpressionGenericMapper<TIn, TOut>
    {
        private static Func<TIn, TOut> FuncCache =null;

        static ExpressionGenericMapper(){
            ParameterExpression s = Expression.Parameter(typeof(TIn), "s");
            List<MemberBinding> memberBindingList = new List<MemberBinding>();
            foreach (var item in typeof(TOut).GetProperties())
            {
                MemberExpression propExp = Expression.Property(s, typeof(TIn).GetProperty(item.Name));//得到 原类 中的属性 即 s.Id, s.Name,s.Age
                MemberBinding memberBinding = Expression.Bind(item, propExp);// 将原类的属性绑定到 目标类属性上  即 Id = s.Id, Name = s.Name,Age = s.Age
                memberBindingList.Add(memberBinding);
            }
            foreach (var item in typeof(TOut).GetFields())
            {
                MemberExpression field = Expression.Property(s, typeof(TIn).GetProperty(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, field);
                memberBindingList.Add(memberBinding);
            }

            MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());//创建一个Body
            Expression<Func<TIn, TOut>> tempExp = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, s);//创建表达式目录树
            FuncCache = tempExp.Compile();//获取委托实例
        }

        public static TOut Trans(TIn t )
        {
            return FuncCache(t);
        }

    }

这里将每一种方法都执行100000次看一下效率:

   Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            for (int i = 0; i < 100000; i++)
            {
                var student = new Student { Id = 1, Name = "张三", Age = 20 };
                var studentCopy5 = new StudentCopy { Id = student.Id, Name = student.Name, Age = student.Age };
            }
            stopwatch.Stop();
            var Common = stopwatch.ElapsedMilliseconds;

            Stopwatch stopwatch2 = new Stopwatch();
            stopwatch2.Start();
            for (int i = 0; i < 100000; i++)
            {
                var studentCopy = ReflectionMapper.Trans<Student, StudentCopy>(new Student { Id = 1, Name = "张三", Age = 20 });
            }
            stopwatch2.Stop();
            var Refection = stopwatch2.ElapsedMilliseconds;

            Stopwatch stopwatch3 = new Stopwatch();
            stopwatch3.Start();
            for (int i = 0; i < 100000; i++)
            {
                var studentCopy2 = SerializeMapper.Trans<Student, StudentCopy>(new Student { Id = 1, Name = "张三", Age = 20 });
            }
            stopwatch3.Stop();
            var Serialize = stopwatch3.ElapsedMilliseconds;

            Stopwatch stopwatch4 = new Stopwatch();
            stopwatch4.Start();
            for (int i = 0; i < 100000; i++)
            {
                var studentCopy3 = ExpressionMapper.Trans<Student, StudentCopy>(new Student { Id = 1, Name = "张三", Age = 20 });
            }

            stopwatch4.Stop();
            var Expression = stopwatch4.ElapsedMilliseconds;

            Stopwatch stopwatch5 = new Stopwatch();
            stopwatch5.Start();
            for (int i = 0; i < 100000; i++)
            {
                var studentCopy4 = ExpressionGenericMapper<Student, StudentCopy>.Trans(new Student { Id = 1, Name = "张三", Age = 20 });
            }
            stopwatch5.Stop();
            var ExpressionGeneric = stopwatch5.ElapsedMilliseconds;


            Console.WriteLine("Common:" + Common);
            Console.WriteLine("Refection:" + Refection);
            Console.WriteLine("Serialize:" + Serialize);
            Console.WriteLine("Expression:" + Expression);
            Console.WriteLine("ExpressionGeneric:" + ExpressionGeneric);

结果:可以看见 直接硬编码的效率是最好的,但是没有通用性,序列化是性能最差的,

静态字典加目录树的方式是性能和硬编码处于同一数量级,且有通用性的方式

猜你喜欢

转载自blog.csdn.net/qq_36445227/article/details/91446081