"Go". NET il implements object deep copy

Source: Internet
Author: User
Tags emit

For deep copies, the usual approach is to serialize the object and then deserialize it into another object. For example, there is a workaround on StackOverflow: https://stackoverflow.com/questions/78536/deep-cloning-objects/78612#78612. This serialized way, for deep copies, is undoubtedly a performance killer.

Today we introduce a deep copy of the framework deepcopy, GitHub address: Https://github.com/ReubenBond/DeepCopy, which was adapted from the Orleans Framework, The implementation logic is very simple.

The implementation of the framework is based on the method of generating the field copy from Il code. The advantage of IL is that you can bypass the syntax rules of C #, such as accessing private objects and readonly assigning values to fields.

Before introducing the framework, let's introduce the IL-related tools.

Il tools

Even if you are not using IL for the first time, it is not an easy task to confirm what IL code can achieve the desired result. This is the tool to help you with the place. You can write code in C # and then copy it to LINQPad, run and open the Il tab in the output.

It is also a good choice to use an anti-compilation/disassembly program like JetBrains's Dotpeek. You can open the compiled assembly in Dotpeek to display IL.

Finally, ReSharper is an indispensable tool. The resharper comes with a convenient IL viewer.

These tools can help you resolve issues with IL, and you can also access official documentation.

Deepcopy

Deepcopy Essentially, it provides only one method:

    public static T Copy<T>(T original);

Deepcopy invoke the sample code:

    List<string> original = new List<string>(2);    original.Add("A"); original.Add("B"); var result = DeepCopier.Copy(original);
Implementation principle

CopyMethod copies each field in the recursive pass-through object to a new instance of the same type. The first thing to deal with is multiple references to the same object, and if the user provides an object that contains its own reference, the result will also contain a reference to itself. This means that we need to perform a reference trace. This is easy to do: We maintain a Dictionary<object, object> mapping from the original object to the Copy object. Our primary method Copy<T>(T orig) will invoke the context method to check the existence of the copied object in the dictionary:

    public static T Copy<T>(T original, CopyContext context)    {      /* TODO: implementation */    }

The copy process is roughly as follows:

    • If it is passed null in, it is returned null ;
    • If the incoming object has been copied, it returns the copied object;
    • If an "immutable object" is passed in, the incoming object is returned directly;
    • If an array is passed in, each element is copied into a new array and returned;
    • Creates a new instance of the incoming type, recursively copying each field from the incoming object to the Copy object and returning it.

The definition of immutable objects is simple: a type is a primitive type,,,, Enum String Guid DateTime ..., or a type that uses a special [Immutable] tag. A more detailed immutable type can refer to the source code, CopyPolicy.cs.

In addition to the last step above, everything else is simple. The final step is to recursively replicate each field, and you can use reflection to get and set the field values. Reflection is a performance killer, so use IL to implement this step.

Il code implementation

DeepCopyThe main IL code in the CopierGenerator.cs class is in the CreateCopier<T>(Type type) method. Let's take a step-by-step secret:

First, create an DynamicMethod object that will save the IL code you created. When you create an DynamicMethod object, you must tell it what the signature is, and here it is a generic delegate type delegate T DeepCopyDelegate<T>(T original, CopyContext context) .

    var dynamicMethod = new DynamicMethod(        type.Name + "DeepCopier",        typeof(T), // 委托返回的类型 new[] {typeof(T), typeof(CopyContext)}, // 委托的参数类型。 typeof(CopierGenerator).Module, true); var il = dynamicMethod.GetILGenerator(); 

Il will become quite complex because it needs to deal with immutable types and value types, and let me explain in 1.1.

    // 定义一个变量来保存返回的结果。    il.DeclareLocal(type);

Next, you need to initialize the new instance of the incoming type to the local variable. There are three scenarios to consider, each of which corresponds to a block in the following code:

    • The type is a value type (struct). Use default(T) an expression to initialize it.
    • The type has a parameterless constructor. new T()initialize it by calling it.
    • There is no parameterless constructor for this type. In this case, we use the. Net framework to resolve, invoke FormatterServices.GetUninitializedObject(type) .
    Constructs the result object instance.var constructorinfo = type. GetConstructor (type.emptytypes);if (type. Isvaluetype) {Value types can be initialized directly.C #: result = Default (T); Il. Emit (opcodes.ldloca_s, (byte) 0); Il. Emit (opcodes.initobj, type); } else if ( constructorinfo! = null) {// If there is a default constructor, the default parameters are used directly. //C #: result = new T (); IL. Emit (Opcodes.newobj, ConstructorInfo); Il. Emit (OPCODES.STLOC_0); } else {//if there is no default constructor exists, use Getuninitializedobject to create the instance. //C #: result = (T) formatterservices.getuninitializedobject (type); IL. Emit (Opcodes.ldtoken, type); Il. Emit (Opcodes.call, DeepCopier.MethodInfos.GetTypeFromHandle); Il. Emit (Opcodes.call, this.methodinfos.getuninitializedobject); IL. Emit (Opcodes.castclass, type); Il. Emit (OPCODES.STLOC_0); }

Creates a local variable that is used to save the result, which is a new instance of the incoming type. Before we do anything, we must record a reference to the newly created object. Each parameter is pushed sequentially into the stack and used OpCodes.Call to invoke context.RecordObject(original, result) . Use OpCodes.Call to invoke CopyContext.RecordObject the method, because CopyContext it is a sealed class, otherwise it will be used OpCodes.Callvirt .

    Instances of value types do not have problems with multiple references.Therefore, only reference types are recorded in the context.if (! type. isvaluetype) {//Record object reference. //C #: Context. Recordobject (original, result); Il. emit (opcodes. ldarg_1); //parameter: Context il. emit (opcodes. LDARG_0); //parameter number: original IL. emit (opcodes. LDLOC_0); //the variable Il that is used to save the result locally. emit (opcodes. Call, this.methodinfos. Recordobject); }  

Enumerates each field on an object and generates code that copies the value of the field into the result variable. The process is as follows:

    Copy the value of each field. foreach (var field inThis.copypolicy.Getcopyablefields (Type)) {Loads a reference to the resulting object.if (Type.Isvaluetype) {Value types need to be loaded by address instead of copied onto the stack. Il.Emit (Opcodes.ldloca_s, (Byte)0); }else {IL.Emit (Opcodes.LDLOC_0); }Loads the value of the original Object field. Il.Emit (Opcodes.LDARG_0); Il.Emit (Opcodes.LDFLD, field);If it is an immutable type, it is assigned directly, otherwise a deep copy field is required.if (! This.copypolicy. isshallowcopyable (Field. FieldType) {//copy field using generic method deepcopy.copy<t> (T original, Copycontext context) //C #: copy<t> (field) IL. emit (opcodes. ldarg_1); Il. emit (opcodes. Call, this.methodinfos. Copyinner. makegenericmethod (Field. FieldType)); //assigns the copied value to the field of the result object. Il. emit (opcodes. stfld, field); } 

Return the results and CreateDelegate build the delegate, which you can use directly next.

    // C#: return result;    il.Emit(OpCodes.Ldloc_0);    il.Emit(OpCodes.Ret);        return dynamicMethod.CreateDelegate(typeof(DeepCopyDelegate<T>)) as DeepCopyDelegate<T>;
Summarize

This is the internal logic of the framework and, of course, some of the details are omitted, for example: special handling DeepCopier.cs in arrays;

Of course there are a lot of details that need to be optimized, and you can put your valuable ideas on GitHub.

Reference content:

    • Https://reubenbond.github.io/posts/codegen-2-il-boogaloo

This article transferred from: http://www.cnblogs.com/tdfblog/p/DeepCopy-By-IL.html

"Go". NET il implements object deep copy

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.