C#Reflection IL - 了解如何复制值

时间:2012-03-16 19:57:18

标签: c# reflection.emit il

我正在尝试提高程序某个部分的性能,这涉及在多个线程中反复深度克隆相同的对象图。目前我使用序列化这是一个很好的简单实现,但我想要更快的东西。我遇到了IL克隆的想法,我正在尝试使用here (Whizzo's Blog)找到的代码。

我还没有真正得到IL,所以我希望有人可以帮助一点并向我解释一些东西(我想这是几个问题的第一个问题)。

这里的问题(和b.t.w如果有人有任何好的链接解释操作码和反射更多,那将是很好的,MSDN没有提供很多细节)是如何复制值的?我可以看到构建一个新对象并从堆栈中弹出

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

稍后给出一个感兴趣的字段值,该值以某种方式被复制。我不明白当原始对象似乎没有被引用时我们如何回到原始对象并获取它的值?或者这是LocalBuilder的一些魔力(我不是100%确定它的作用):

// 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);

我稍微修改了代码,因为我总是想要一个Deep克隆,我希望它基于序列化字段:

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);
  }

2 个答案:

答案 0 :(得分:3)

首先你有

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

您的堆栈包含(cloneVariable,myObject)

enerator.Emit(OpCodes.Ldfld, field);

这个弹出对象引用,从字段中检索值并将值压入堆栈

您的堆栈包含(cloneVariable,myObject.field)

generator.Emit(OpCodes.Stfld, field);

这个弹出对象引用和值,并将值存储在对象的字段中。结果相当于C#的

cloneVariable.field = myObject.field

答案 1 :(得分:0)

¿不会更容易使用结构而不是类并直接将类型的字节数组编组到新的对象内存中?

如果可以通过编组直接进行,但我几乎 - (总是保持门打开以逃避嘿嘿),我真的不是真的 - 确保它可以通过编译/不安全来完成,将指针投射到byte *并将这些字节复制到目标结构指针。

[编辑]忘记指针,只需通过编组就可以做到不安全。查看这篇文章;

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

[Edit2]我必须学会一些耐心:P你的解决方案要快得多。