1. Demand
In the code you will often encounter the need to copy the object again, or the property name of the same value copied again.
Like what:
1 Public classStudent2 {3 Public intId {Get;Set; }4 Public stringName {Get;Set; } 5 Public intAge {Get;Set; } 6 }7 8 Public classStudentsecond9 {Ten Public intId {Get;Set; } One Public stringName {Get;Set; } A Public intAge {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};
or assign a value to a property of another class Studentsecond, the name and type of the two class property are the same.
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. But the repetition rate of the code is too high, and the code looks not beautiful, more important is a waste of time, if a class has dozens of attributes , that one property assignment is not a waste of energy, such as the repetition of the work should be optimized.
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.
1 Private StaticTOut Transreflection<tin, tout>(Tin Tin)2 {3TOut TOut = activator.createinstance<tout>();4 varTintype =Tin.gettype ();5 foreach(varItemoutinchTout.gettype (). GetProperties ())6 {7 varItemIn =Tintype.getproperty (itemout.name);8 if(ItemIn! =NULL)9 {Ten Itemout.setvalue (TOut, Itemin.getvalue (tIn)); One } A } - 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
From this we can see that the efficiency of serialization and reflection is not very different.
3. Expression Tree 3.1, Introduction
About the expression tree do not understand can Baidu.
This means that copying an object can also be used in the expression tree.
1 New Studentsecond {age = x.age, Id = x.id, Name = X.name}; 2 var f = ss.compile (); 3 Studentsecond Studentsecond = f (s);
In this way we can achieve the same effect.
Some people say that this kind of writing and the original copy is no different, the code is more, this is only the first step.
3.2. Analysis Code
We use Ilspy to decompile This expression code as follows:
1 parameterexpression parameterexpression;2Expression<func<student, studentsecond>> ss = Expression.lambda<func<student, StudentSecond> > (Expression.memberinit (Expression.new (typeof(Studentsecond)),Newmemberbinding[]3 {4Expression.bind (Methodof (Studentsecond.set_age (int)) , Expression.property (ParameterExpression, Methodof (Student.get_age () )),5Expression.bind (Methodof (studentsecond.set_id (int)) , Expression.property (ParameterExpression, Methodof (student.get_id () )),6Expression.bind (Methodof (Studentsecond.set_name (string)) , Expression.property (ParameterExpression, Methodof (Student.get_name () )))7}),Newparameterexpression[]8 {9 parameterexpressionTen }); OneFunc<student, studentsecond> f =ss.compile (); AStudentsecond Studentsecond = f (s);
So that means we just loop all the properties with reflection and then expression.bind all the properties . Finally call compile () (s) to get the correct studentsecond.
See this some people asked again, if the reflection is not 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 is only necessary for the first time to use the reflection, and later use will not need to reflect.
3.3. Copy object Generic Code
For versatility, the student and Studentsecond are replaced by generics respectively.
1 Private Staticdictionary<string,Object> _dic =Newdictionary<string,Object>();2 3 Private StaticTOut Transexp<tin, tout>(Tin Tin)4 {5 stringKey =string. Format ("Trans_exp_{0}_{1}",typeof(TIn). FullName,typeof(TOut). FullName);6 if(!_dic.containskey (key))7 {8ParameterExpression parameterexpression = Expression.parameter (typeof(TIn),"P");9List<memberbinding> memberbindinglist =NewList<memberbinding>();Ten One foreach(varIteminch typeof(TOut). GetProperties ()) A { - - if(!item. CanWrite) the 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 ()); AExpression<func<tin, tout>> lambda = expression.lambda<func<tin, tout>> ( Memberinitexpression,Newparameterexpression[] {parameterexpression}); atFunc<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.
1 Public Static classTransexpv2<tin, tout>2 {3 4 Private Static ReadOnlyFunc<tin, tout> cache =Getfunc ();5 Private StaticFunc<tin, tout>Getfunc ()6 {7ParameterExpression parameterexpression = Expression.parameter (typeof(TIn),"P");8List<memberbinding> memberbindinglist =NewList<memberbinding>();9 Ten foreach(varIteminch typeof(TOut). GetProperties ()) One { A if(!item. CanWrite) - Continue; - theMemberexpression 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}); A at returnlambda.compile (); - } - - Public StaticTOut Trans (Tin Tin) - { - returncache (tIn); in } - to}
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.
Original: http://www.cnblogs.com/emrys5/p/expression_trans_model.html
C # quickly and efficiently copy objects another way expression tree