Expression expression tree two

Topic: How to convert the two class attribute fields the same: convert Student to StudentCopy

Here are two methods:

One

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

    }
}

two

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

Both of the above can be achieved, but one of the above two methods is to find attributes and fields through reflection, and the other is to convert strings through reflection, which is not performance friendly.

Is there a way that is universal and has good performance:

Idea: Use the expression directory tree to achieve, in fact, the decompilation tool can decompile the following shortcuts into a normal directory tree declaration method, just copy it and change it.


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

You need to understand the meaning of this method and the following code:

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

Here we implement it:

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

    }

Three methods can achieve the purpose, but the way of using the directory tree is the best performance

Although it is inevitable to use reflection to find attributes and fields for the first time, there is a static Dictionary as a cache

Good performance and universal

 There is no difference between directly writing code and copying, but the implementation code is relatively complicated, and you can see that it can be completed smoothly.

Here you can also combine the previously learned static dictionary because the efficiency of the Dictionary class and the static dictionary is not comparable, as follows: each class from the static dictionary is independent and does not need to be like

Dictionary also has to look up here, the search will consume a lot of performance

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

    }

Here, each method is executed 100,000 times to see the efficiency:

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

Result: It can be seen that the efficiency of direct hard coding is the best , but there is no versatility, and serialization is the worst performance .

The way of adding a static dictionary to a directory tree is that the performance and hard coding are in the same order of magnitude, and there is a universal way

 

Guess you like

Origin blog.csdn.net/qq_36445227/article/details/91446081