Я пишу код, который маршалирует любую структуру в байтовый массив. У меня есть метод:IL Emit struct serializer
public static byte[] Serialize(MyStruct value)
{
IntPtr p = new IntPtr(&value);
byte[] result = new byte[12];
Marshal.Copy(p, result, 0, result.Length);
return result;
}
Вот IL для этого кода:
.method public hidebysig static uint8[] Serialize(valuetype Program/MyStruct 'value') cil managed
{
// code size: 37 (0x25)
.maxstack 4
.locals init ([0] native int p,
[1] uint8[] result,
[2] uint8[] V_2)
IL_0000: nop
IL_0001: ldloca.s p
IL_0003: ldarga.s 'value'
IL_0005: conv.u
IL_0006: call instance void [mscorlib]System.IntPtr::.ctor(void*)
IL_000b: ldc.i4.s 12
IL_000d: newarr [mscorlib]System.Byte
IL_0012: stloc.1
IL_0013: ldloc.0
IL_0014: ldloc.1
IL_0015: ldc.i4.0
IL_0016: ldloc.1
IL_0017: ldlen
IL_0018: conv.i4
IL_0019: call void [mscorlib]System.Runtime.InteropServices.Marshal::Copy(native int,
uint8[],
int32,
int32)
IL_001e: nop
IL_001f: ldloc.1
IL_0020: stloc.2
IL_0021: br.s IL_0023
IL_0023: ldloc.2
IL_0024: ret
} // end of method MyStruct::Serialize
теперь я пытаюсь испускают общий метод:
private static class SerializationHolder<T> where T : struct
{
public static readonly Func<T, byte[]> Value = CreateDelegate();
private static Func<T, byte[]> CreateDelegate()
{
var dm = new DynamicMethod("Serialize" + typeof (T).Name,
typeof (byte[]),
new[] {typeof (T)},
Assembly.GetExecutingAssembly().ManifestModule);
const string parameterName = "value";
dm.DefineParameter(1, ParameterAttributes.None, parameterName);
var generator = dm.GetILGenerator();
var p = generator.DeclareLocal(typeof (IntPtr));
generator.DeclareLocal(typeof (byte));
generator.DeclareLocal(typeof (byte));
generator.Emit(OpCodes.Ldloca_S, p);
generator.Emit(OpCodes.Ldarga_S, parameterName);
generator.Emit(OpCodes.Conv_U);
var intPtrCtor = typeof (IntPtr).GetConstructor(new[] {typeof(void*)});
Debug.Assert(intPtrCtor != null);
generator.Emit(OpCodes.Call, intPtrCtor);
var sizeInBytes = Marshal.SizeOf(typeof (T));
generator.Emit(OpCodes.Ldc_I4_S, sizeInBytes);
generator.Emit(OpCodes.Newarr, typeof (byte));
generator.Emit(OpCodes.Stloc_1);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldc_I4_0);
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldlen);
generator.Emit(OpCodes.Conv_I4);
var marshalCopy = typeof (Marshal).GetMethod("Copy", new[] {typeof (IntPtr), typeof (byte[]), typeof (int), typeof (int)});
generator.EmitCall(OpCodes.Call, marshalCopy, null);
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Stloc_2);
generator.Emit(OpCodes.Ldloc_2);
generator.Emit(OpCodes.Ret);
return (Func<T, byte[]>)dm.CreateDelegate(typeof(Func<T, byte[]>));
}
}
, но когда я пытаюсь назовите его с ошибкой CLR detected an invalid program
. Я думаю, что проблема в этой строке:
generator.Emit(OpCodes.Ldarga_S, parameterName);
но если я пишу:
generator.Emit(OpCodes.Ldarga_S, 0);
, если не удается с NullReferenceException
Теперь у меня есть код, который сделает это с общий тип T, но он использует недокументированные ключевые слова
public static byte[] Serialize<T>(this T value) where T : struct
{
TypedReference tr = __makeref(value);
IntPtr p = *(IntPtr*)&tr;
int sizeInBytes = Marshal.SizeOf(typeof(T));
byte[] result = new byte[sizeInBytes];
Marshal.Copy(p, result, 0, result.Length);
return result;
}
Это неустойчиво и может сломаться в новых выпусках .Net, поэтому я хочу заменить его кодом на основе Emit
[Это помогает] (http://stackoverflow.com/a/25311889/2530848)? –
@SriramSakthivel нет, потому что нет проблемы с методом Serialize (а затем с Marshal.Copy), это проблема с его испусканием. –
Я понимаю, но почему вам нужно испускать динамический метод на первом месте? Мой ответ в данной ссылке является общим, вы можете использовать его. –