【发布时间】:2015-02-25 19:34:38
【问题描述】:
我正在编写将任何结构编组为字节数组的代码。我有一个方法:
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 的代码替换它
【问题讨论】:
-
@SriramSakthivel 不,因为方法 Serialize(然后是 Marshal.Copy)没有问题,发出它的问题。
-
我明白了,但是为什么首先需要发出动态方法?我在给定链接中的答案是通用的,您可以使用它。
-
因为我无法在 C# 中获取未知泛型类型 T 的地址,所以可以使用 IL 来完成。见编辑
-
当我给你一个没有不安全代码的解决方案时,我不明白为什么你需要发出一个方法(这也是不安全的)?你的目标是什么?将任何结构转换为 byte[] 对吗?我的代码正是这样做的。
标签: c# .net .net-4.5 code-generation reflection.emit