C# Reflection IL - Understanding how values are copied

本秂侑毒 提交于 2019-12-12 02:27:51

问题


I'm trying to improve the performance of a certain part of my program which involves deep cloning the same object graph over and over across multiple threads. Currently I use serialization which is a nice easy implementation but I'd like something faster. I came across the idea of IL cloning and am trying to work with some code found here (Whizzo's Blog).

I don't really get IL as yet, so I'm hoping someone can help a little bit and explain some of the stuff to me (I imagine this is the first question of several).

The question here (and b.t.w if anyone has any good links explaining opcodes and reflection.emit a bit more that would be great, MSDN doesn't give a lot of detail) is how are the values copied? I can see that a new object is constructed and popped from the stack

generator.Emit(OpCodes.Newobj, cInfo);
generator.Emit(OpCodes.Stloc, cloneVariable);

Then a little bit later given a field value of interest, the value is somehow copied. I don't understand how we go back to the original object and grab it's value when the original object doesn't seem to be referenced? Or is this some magic of the LocalBuilder (I'm not 100% sure what it does):

// I *THINK* this Pushes the cloneVariable on the stack, loads an argument (from where?) and sets the field value based on the FieldInfo??
generator.Emit(OpCodes.Ldloc, cloneVariable);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Stfld, field);

I've modified the code slightly as I always want a Deep clone and I want it based on serialized fields:

private static T CloneObjectWithILDeep(T myObject)
{
   Delegate myExec = null;
   if (!_cachedILDeep.TryGetValue(typeof(T), out myExec))
   {
     // Create ILGenerator            
     DynamicMethod dymMethod = new DynamicMethod("DoDeepClone", typeof(T), new Type[] { typeof(T) }, Assembly.GetExecutingAssembly().ManifestModule, true);
     ILGenerator generator = dymMethod.GetILGenerator();
     LocalBuilder cloneVariable = generator.DeclareLocal(myObject.GetType());

     ConstructorInfo cInfo = myObject.GetType().GetConstructor(Type.EmptyTypes);
     generator.Emit(OpCodes.Newobj, cInfo);
     generator.Emit(OpCodes.Stloc, cloneVariable);

     foreach (FieldInfo field in typeof(T).GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public))
     {         
        if(field.IsNotSerialized)
            continue;

        if (field.FieldType.IsValueType || field.FieldType == typeof(string))
        {
           generator.Emit(OpCodes.Ldloc, cloneVariable);
           generator.Emit(OpCodes.Ldarg_0);
           generator.Emit(OpCodes.Ldfld, field);
           generator.Emit(OpCodes.Stfld, field);
         }
         else if (field.FieldType.IsClass)
         {
           CopyReferenceType(generator, cloneVariable, field);
         }
       }

      generator.Emit(OpCodes.Ldloc_0);
      generator.Emit(OpCodes.Ret);
      myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));
      _cachedILDeep.Add(typeof(T), myExec);
    }
    return ((Func<T, T>)myExec)(myObject);
  }

回答1:


First you have

generator.Emit(OpCodes.Ldloc, cloneVariable);
generator.Emit(OpCodes.Ldarg_0);

Your stack contains (cloneVariable, myObject)

enerator.Emit(OpCodes.Ldfld, field);

This one pops object reference, retrieves the value from the field and pushes the value onto the stack

Your stack contains (cloneVariable, myObject.field)

generator.Emit(OpCodes.Stfld, field);

This one pops object reference and value and stores the value in the object's field. The result is equivalent to C#'s

cloneVariable.field = myObject.field



回答2:


¿Won't be easier to use structs instead of classes and directly marshall the byte array of the type into the new object memory?

I'm not really shure if that can be done straight forward with Marshalling but I am almost-(always keep the door open to run away hehe)-shure it can be done by compiling with /unsafe, casting the pointer to byte* and copy those bytes to the target struct pointer.

[Edit] Forget about the pointers, you can do it without unsafe just by marshalling. Check this post;

How to convert a structure to a byte array in C#?

[Edit2] I have to learn some patience :P your solution is much faster.



来源:https://stackoverflow.com/questions/9743724/c-sharp-reflection-il-understanding-how-values-are-copied

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!