C# Fast and Efficient Replication of Objects Another Way to Reproduce Expression Trees

Keywords: C# Lambda JSON Attribute xml

1. Demand

In code, you often encounter the need to copy the object or the same value of the attribute name.

For example:

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; } 
        public int Age { get; set; } 
    }

    public class StudentSecond
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; } 
    }

Student s = new Student() { Age = 20, Id = 1, Name = "Emrys" };

We need to assign values to the new Student

Student ss = new Student { Age = s.Age, Id = s.Id, Name = s.Name };

Or assign a value to the property of another class StudentSecond whose name and type are identical.

StudentSecond ss = new StudentSecond { Age = s.Age, Id = s.Id, Name = s.Name };

 

2. Solutions

Of course, the most primitive way is to write all the attributes that need to be assigned manually. Such efficiency is the highest. But such code is too repetitive, and the code looks ugly. More importantly, it is a waste of time. If there are dozens of attributes in a class, it is not a waste of energy to assign attributes. Work like this should be optimized.

2.1. Reflection

Reflection should be a method that many people have used, that is, encapsulating a class, reflecting to get attributes and setting the values of attributes.

        private static TOut TransReflection<TIn, TOut>(TIn tIn)
        {
            TOut tOut = Activator.CreateInstance<TOut>();
            foreach (var itemOut in tOut.GetType().GetProperties())
            {
                var itemIn = tIn.GetType().GetProperties().Where(i => i.Name == itemOut.Name).FirstOrDefault();
                if (itemIn != null)
                {
                    itemOut.SetValue(tOut, itemIn.GetValue(tIn));
                }
            }
            return tOut;
        }

Call: Student Second s s = TransReflection < Student, Student Second > (s);

One million calls: 2464 milliseconds

 

2.2. Serialization

There are many ways to serialize, such as binary, xml, json and so on. Today we will use Newtonsoft's json for testing.

Call: StudentSecond s s = JsonConvert. DeserializeObject < StudentSecond > (JsonConvert. SerializeObject (s));

One million calls: 2984 milliseconds

It can be seen from this that there is little difference between serialization and reflection efficiency.

 

3. Expression Tree

3.1. Introduction

You can Baidu if you don't know about the expression tree.

That is to say, copying objects can also be done in the form of expression trees.

        Expression<Func<Student, StudentSecond>> ss = (x) => new StudentSecond { Age = x.Age, Id = x.Id, Name = x.Name };
        var f = ss.Compile();
        StudentSecond studentSecond = f(s);

In this way we can achieve the same effect.

Some people say that there is no difference between this writing and the original copy, but there is more code. This is only the first step.

 

3.2. Analysis code

We use ILSpy to decompile the expression code as follows:

   ParameterExpression parameterExpression;
    Expression<Func<Student, StudentSecond>> ss = Expression.Lambda<Func<Student, StudentSecond>>(Expression.MemberInit(Expression.New(typeof(StudentSecond)), new MemberBinding[]
    {
        Expression.Bind(methodof(StudentSecond.set_Age(int)), Expression.Property(parameterExpression, methodof(Student.get_Age()))),
        Expression.Bind(methodof(StudentSecond.set_Id(int)), Expression.Property(parameterExpression, methodof(Student.get_Id()))),
        Expression.Bind(methodof(StudentSecond.set_Name(string)), Expression.Property(parameterExpression, methodof(Student.get_Name())))
    }), new ParameterExpression[]
    {
        parameterExpression
    });
    Func<Student, StudentSecond> f = ss.Compile();
    StudentSecond studentSecond = f(s);

That is to say, we just loop all the attributes with reflection and then express. Bind all the attributes. Finally, call Compile()(s) to get the correct StudentSecond.

Seeing these people ask again, isn't it inefficient to use reflection, which is no different from using reflection directly or serialization?

Of course, the solution is that our expression tree can be cached. It's just that reflection is needed for the first time, and no reflection is needed for later use.

 

3.3. Common Code for Copied Objects

Student and Student Second are generic substitutes for generality.

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

        private static TOut TransExp<TIn, TOut>(TIn tIn)
        {
            string key = string.Format("trans_exp_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName);
            if (!_Dic.ContainsKey(key))
            {
                ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
                List<MemberBinding> memberBindingList = new List<MemberBinding>();

                foreach (var item in typeof(TOut).GetProperties())
                {
                    MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
                    MemberBinding memberBinding = Expression.Bind(item, property);
                    memberBindingList.Add(memberBinding);
                }

                MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
                Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression });
                Func<TIn, TOut> func = lambda.Compile();

                _Dic[key] = func;
            }
            return ((Func<TIn, TOut>)_Dic[key])(tIn);
        }

Call: StudentSecond s s= TransExp < Student, StudentSecond > (s);

One million calls: 564 milliseconds

 

4. Summary

From the above test and analysis, it can be easily concluded that using expression tree is one of the methods that can achieve both efficiency and writing style. In a word, it is better than traditional serialization and reflection.

Finally, I hope it will be helpful to you. This article is original. Welcome to make bricks and recommendation.  

Posted by MHardeman25 on Fri, 12 Jul 2019 18:14:29 -0700