我正在尝试编写一个动态方法,它接受
ReadOnlyMemory<byte>
并处理它。但当我试图分割记忆时,我经历了非常奇怪的行为。切片(或同一上下文中的任何其他函数)的参数似乎已损坏。这是重现该问题的顶级语句示例。
using System.Reflection.Emit;
DynamicMethod m = new DynamicMethod("EMITD_arraydeser_" + typeof(int[]), null, new Type[1] { typeof(ReadOnlyMemory<byte>) }, true);
var il = m.GetILGenerator();
il.DeclareLocal(typeof(ReadOnlyMemory<byte>));
il.Emit(OpCodes.Ldarg, 0);
il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ldc_I4, 4);
il.EmitWriteLine("checkSlice_1....");
il.Emit(OpCodes.Call, typeof(Helpers).GetMethod("checkSlice")); // This works as expected, with mem = ReadOnlyMemory<byte>[16], start = 0, end = 4
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldarg, 0);
il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ldc_I4, 4);
il.EmitWriteLine("checkSlice_2....");
il.Emit(OpCodes.Call, typeof(Helpers).GetMethod("checkSlice")); // This gets corrupted, with start = 4, end = random value and mem not being readable
// Print the result
il.Emit(OpCodes.Call, typeof(ReadOnlyMemory<byte>).GetMethod("ToArray"));
il.Emit(OpCodes.Call, typeof(BitConverter).GetMethod("ToString", new Type[] { typeof(byte[]) }));
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
il.Emit(OpCodes.Ldarga_S, 0);
il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ldc_I4, 4);
il.EmitWriteLine("slice_1....");
il.Emit(OpCodes.Call, typeof(ReadOnlyMemory<byte>).GetMethod("Slice", new Type[2] {typeof(int), typeof(int)})); // throws ArgumentOutOfRangeException
// Print the result
il.Emit(OpCodes.Call, typeof(ReadOnlyMemory<byte>).GetMethod("ToArray"));
il.Emit(OpCodes.Call, typeof(BitConverter).GetMethod("ToString", new Type[] { typeof(byte[]) }));
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
il.Emit(OpCodes.Ret);
var del = m.CreateDelegate<Action<ReadOnlyMemory<byte>>>();
var s = new byte[] {
3,0,0,0,
0xAA,0xBB,0,0,
0xCC,0xDD,0,0,
0xEE,0xFF,0,0,
};
ReadOnlyMemory<byte> buff = new(s);
del(buff);
class Helpers
{
public static ReadOnlyMemory<byte> checkSlice(ReadOnlyMemory<byte> mem, int start, int end)
{
try
{
Console.WriteLine($"{start}:{end}, {mem}");
return mem.Slice(start, end);
}
catch (Exception)
{
Console.WriteLine("Exception on slice...." + start + ":" + end);
return new ReadOnlyMemory<byte>();
}
}
}
这是实际输出:
checkSlice_1....
0:4, System.ReadOnlyMemory<Byte>[16]
checkSlice_2....
Exception on slice....4:1616289928
slice_1....
Unhandled exception. System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'start')
at EMITD_arraydeser_System.Int32[](ReadOnlyMemory`1)
at Program.<Main>$(String[] args) in C:\PATH\Program.cs:line 49
我怀疑它与我用来打印ReadOnlyMemory(在“
// Print the result
”注释下)的3行有关,我从切片返回(mb因为ReadOnlyMemory是一个值类型?),但我不知道了解这如何影响之前执行的函数调用。遗憾的是,据我所知,无法查看生成的实际 IL。
我对 IL 的了解非常有限,而且我发现参考文献很难理解,所以我希望比我聪明的人可以解释它是如何工作的。 :)
因为 ReadOnlyMemory
var il = m.GetILGenerator();
var local = il.DeclareLocal(typeof(ReadOnlyMemory<byte>)); // Save the LocalBuilder
il.Emit(OpCodes.Ldarg, 0);
il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ldc_I4, 4);
il.Emit(OpCodes.Call, typeof(Helpers).GetMethod("checkSlice"));
il.Emit(OpCodes.Stloc, local); // Save the ReadOnlyMemory<byte> instance to the local variable
// Print the result
il.Emit(OpCodes.Ldloca, local); // IMPORTANT: Push the address of the local variable
il.Emit(OpCodes.Call, typeof(ReadOnlyMemory<byte>).GetMethod("ToArray"));
il.Emit(OpCodes.Call, typeof(BitConverter).GetMethod("ToString", new Type[] { typeof(byte[]) }));
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
il.Emit(OpCodes.Ret);
您的原始代码导致 ReadOnlyMemory