1. Demand
It is often necessary to copy objects into a new object in the project code, or to copy the values of the same property names once.
Like what:
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 = $, Id = 1, Name = "Emrys"};
We need to assign a new student.
Student ss = new Student {age = s.age, Id = s.id, Name = s.name};
Assigns a value to the property of another class Studentsecond, with the names and types of the two class properties consistent.
Studentsecond ss = new Studentsecond {age = s.age, Id = s.id, Name = s.name};
2. Solutions
The most primitive approach, of course, is to manually hand-write all properties that need to be assigned. This is the highest efficiency. However, the repetition rate of the code is too high, more important is a waste of time, if a class has dozens of attributes, that one property assignment is too wasteful, like this duplication of work should be more need to optimize.
2.1. Reflection
Reflection should be a method used by many people, that is, encapsulating a class, reflecting the Get property and setting the value of the property.
Private StaticTOut Transreflection<tin, tout>(Tin Tin) {TOut TOut= activator.createinstance<tout>(); varTintype =Tin.gettype (); foreach(varItemoutinchTout.gettype (). GetProperties ()) {varItemIn =Tintype.getproperty (itemout.name); if(ItemIn! =NULL) {Itemout.setvalue (TOut, Itemin.getvalue (tIn)); } } returnTOut; }
Call:studentsecond ss= transreflection<student, studentsecond> (s);
Call 1 million times time: 2464 milliseconds
2.2. Serialization
There are many ways to serialize, with binary, XML, JSON, and so on, and today we're testing with newtonsoft json.
Call:studentsecond ss= jsonconvert.deserializeobject<studentsecond> ( Jsonconvert.serializeobject (s));
call 1 million times time:2984 milliseconds
3. Expression Tree 3.1, Introduction
About the expression tree do not understand can Baidu.
This means that copying objects can also be used in the expression tree
New Studentsecond {age = x.age, Id = x.id, Name = x.name}; var f = ss.compile (); = f (s);
In this way we can achieve the same effect. It seems that there is no difference between this and the original copy, but the code is much more, but this is only the first step. Follow!!!!!!!!!!!!!!!!!!!!!!!.
3.2. Analysis Code
Use Ilspy to decompile This expression code as follows:
parameterexpression parameterexpression; Expression<func<student, studentsecond>> ss = Expression.lambda<func<student, StudentSecond>> ( Expression.memberinit (Expression.new (typeof(Studentsecond)),Newmemberbinding[] {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 () ))),Newparameterexpression[] {parameterexpression}); Func<student, studentsecond> f =Ss.compile (); Studentsecond Studentsecond= f (s);
Just loop all the properties with reflection and then expression.bind all the properties . Finally call compile () (s) to get the correct studentsecond.
Some people have to ask again, if the reflection is not very inefficient, and direct use of reflection or serialization with no difference?
Of course this can be solved, is that our expression tree can be cached . It's just a reflection when you use it for the first time, and you don't need to reflect it later.
3.3. Copy object Generic Code
For versatility, the student and Studentsecond are replaced by generics respectively.
Private Staticdictionary<string,Object> _dic =Newdictionary<string,Object>(); Private StaticTOut Transexp<tin, tout>(Tin Tin) {stringKey =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 =NewList<memberbinding>(); foreach(varIteminch typeof(TOut). GetProperties ()) {if(!item. CanWrite)Continue; 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,Newparameterexpression[] {parameterexpression}); Func<tin, tout> func =Lambda.compile (); _dic[key]=func; } return(Func<tin, tout>) _dic[key]) (tIn); }
Call:studentsecond ss= transexp<student, studentsecond> (s);
call 1 million times time: 564 milliseconds
3.4. Re-optimize the code with generic features
Store the cache without a dictionary, because generics can easily solve this problem.
Public Static classTransexpv2<tin, tout> { Private Static ReadOnlyFunc<tin, tout> cache =Getfunc (); Private StaticFunc<tin, tout>Getfunc () {parameterexpression parameterexpression= Expression.parameter (typeof(TIn),"P"); List<MemberBinding> memberbindinglist =NewList<memberbinding>(); foreach(varIteminch typeof(TOut). GetProperties ()) {if(!item. CanWrite)Continue; 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,Newparameterexpression[] {parameterexpression}); returnLambda.compile (); } Public StaticTOut Trans (Tin Tin) {returncache (tIn); } }
Call:studentsecond ss= transexpv2<student, Studentsecond>. Trans (s);
call 1 million times time: 107 milliseconds
It takes far less than 338 milliseconds to use AutoMapper.
4. Summary
From the above test and analysis can be easily concluded that the expression tree is a way to achieve both efficiency and writing methods , in short, than the traditional serialization and reflection more excellent.
The last hope is helpful to you.
Reference point: http://www.cnblogs.com/emrys5/
Go How to quickly and efficiently replicate objects in C #