Emit, dynamicmethod, and field Replication

Source: Internet
Author: User
Tags emit

Project requirements: sometimes it is necessary to convert the inheritance class to the base class. Some people have said, isn't it possible to use it directly? No conversion is required. Yes, in many cases this is the case, but sometimes it is not that easy to write methods for multiple subclasses as abstract classes. Or the parameter is an interface that needs to be converted to an object or something that needs to be met in some cases.

 

I used to synchronize all attributes. Later, I found that the effect was incomplete. Some fields were not disclosed, which would affect some external representations of the class. As mentioned in the previous article, the data of the class is stored in the field, so we can simply copy the field.

 

Direct reflection is very simple,CodeIt is also very short. Let's take a look.

 

 

Public static ttarget converttobyfields <ttarget> (Object SRC) Where ttarget: Class, new () {var srctype = SRC. getType (); var targettype = typeof (ttarget); If (srctype = targettype) return SRC as ttarget; If (src = NULL) return default (ttarget ); vaR srcfields = srctype. getfields (bindingflags. nonpublic | bindingflags. public | bindingflags. instance); var targetfields = targettype. getfields (bindingfl AGS. nonpublic | bindingflags. public | bindingflags. instance); var commonfields = srcfields. where (F => targetfields. any (tf => TF. name = f. name & TF. fieldtype = f. fieldtype )&&! F. isinitonly &&! F. isliteral ). tolist (); var target = activator. createinstance <ttarget> (); commonfields. foreach (F => F. setvalue (target, F. getvalue (SRC); Return target ;}

However, experts all say that reflection is very inefficient, so we should refer to the opinions of experts to see if we can change it to an emit or expression method with higher efficiency. Consider: expression only supports a single statement, so it is definitely not feasible to operate on multiple fields unless you define a Lambda for each fieldinfo, which is quite painful, in addition, if you want to use dynamicmethod, it is definitely not as efficient as a dynamicmethod. We can use dynamicmethod to implement it! ~

 

The first idea is to create a method that accepts the SRC parameter input of an object type and instantiate a targettype object in the method, assign the value of all fields of SRC to this target and return the result. Well, this is our purpose.

 

Check the Code:

 

// Delegate dictionary for caching. Static dictionary <string, delegate> _ genericdelegates = new dictionary <string, delegate> (); public static ttarget converttobyfields <ttarget> (Object SRC, bool isclone) Where ttarget: class, new () {var srctype = SRC. getType (); var targettype = typeof (ttarget); // check whether the operation is necessary. If (! Isclone & srctype = targettype) return SRC as ttarget; If (src = NULL) return default (ttarget ); // generate a unique key var key = "copyfieldsfrom _" + srctype. fullname + "_ to _" + targettype. fullname; # region emit if (! _ Delegates. containskey (key) {// find the common field var srcfields = srctype. getallfields (bindingflags. nonpublic | bindingflags. public | bindingflags. instance); var targetfields = targettype. getallfields (bindingflags. nonpublic | bindingflags. public | bindingflags. instance); var commonfields = srcfields. where (F => targetfields. any (tf => TF. name = f. name & TF. fieldtype = f. fieldtype )&&! F. isinitonly &&! F. isliteral ). tolist (); // create dynamicmethod. Note that skipvisiblity must be set to true. vaR dm = new dynamicmethod (Key, methodattributes. public | methodattributes. static, callingconventions. standard, targettype, new type [] {typeof (object)}, srctype, true); var IL = DM. getilgenerator (); Il. declarelocal (srctype); Il. declarelocal (targettype); Il. declarelocal (targettype); Il. emit (Opcodes. NOP); Il. emit (Opcodes. ldarg_0); Il. emit (Opcodes. castclass, srctype); Il. emit (Opcodes. stloc_0); Il. emit (Opcodes. newobj, targettype. getconstructor (New Type [] {}); Il. emit (Opcodes. stloc_1); commonfields. foreach (F => {Il. emit (Opcodes. ldloc_1); Il. emit (Opcodes. ldloc_0); Il. emit (Opcodes. ld1_, F); Il. emit (Opcodes. stfiel, targetfields. first (tf => TF. name = f. name) ;}); Il. emit (Opcodes. ldloc_1); Il. emit (Opcodes. stloc_2); Il. emit (Opcodes. ldloc_2); Il. emit (Opcodes. RET); var Lambda = (func <object, ttarget>) DM. createdelegate (typeof (func <object, ttarget>); _ delegates [Key] = Lambda ;}# endregion return (func <object, ttarget>) _ delegates [Key]) (SRC );}

 

The above Code does not have any technical content. I wrote this reflectori ~ In fact, it is super simple, but the main thing is

 

 
Commonfields. foreach (F => {Il. emit (Opcodes. ldloc_1); Il. emit (Opcodes. ldloc_0); Il. emit (Opcodes. ld1_, F); Il. emit (Opcodes. stfiel, targetfields. first (tf => TF. name = f. name ));});

To put it bluntly, it is getvalue and setvalue. Haha. In addition, you must pay attention to readonly and constants when filtering commonfields.

 

The only thing worth mentioning is that the skpvisiblity parameter when creating dynamicmethod must be set to true, otherwise fieldaccessexception will be reported, because the field is private!

 

 

--------------------------------------------

Update: Because the getfields Value Returns the type field but does not return the upper-level field, this extension method is required:

 

Private Static ienumerable <fieldinfo> getallfields (this type, bindingflags flags) {var fields = new dictionary <string, fieldinfo> (); var typenow = type; do {var FS = typenow. getfields (flags ). tolist (); FS. foreach (F => {If (! Fields. containskey (F. Name + "! "+ F. fieldtype. fullname) fields. Add (F. Name + "! "+ F. fieldtype. fullname, f) ;}); typenow = typenow. basetype ;}while (typenow! = NULL); Return fields. Values ;}

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.