如何通过反射API正确调用MemoryMarshal.CreateSpan方法
异常原因
你写的反射调用抛错的核心原因是MemoryMarshal.CreateSpan<T>的第一个参数是ref T 托管引用参数,你直接把装箱后的结构体对象作为参数传入Invoke时,反射默认会做参数值拷贝,无法将原结构体实例的实际引用正确传递给ref形参,运行时不支持这种传参逻辑,自然抛出NotSupportedException。
实现方案
不需要unsafe关键字、不需要直接操作指针,借助反射调用Unsafe.Unbox拿到装箱值类型的内部托管引用,再依次调用CreateSpan、AsBytes方法,最后将得到的字节Span转为普通字节数组返回即可,全程不需要提前预定义对应泛型类型。
- 注意:
Span<T>是栈上的ref结构,不能直接作为object返回,必须将其内容拷贝到堆上的字节数组才能正常作为结果返回 - 所有方法调用都通过反射动态构造泛型版本,兼容任意值类型,不需要提前知道结构体定义
完整实现代码如下:
using System.Reflection; using System.Runtime.InteropServices; private static byte[] GetBytes(object value) { if (value == null) throw new ArgumentNullException(nameof(value)); Type structType = value.GetType(); if (!structType.IsValueType) throw new ArgumentException("仅支持值类型传入", nameof(value)); // 加载需要的类型和方法 Type mmType = typeof(MemoryMarshal); Type unsafeType = Assembly.Load("System.Runtime.CompilerServices.Unsafe") .GetType("System.Runtime.CompilerServices.Unsafe"); BindingFlags staticPub = BindingFlags.Public | BindingFlags.Static; // 第一步:拿到装箱结构体内部实际存储位置的托管引用,解决ref参数传参问题 MethodInfo unboxMethod = unsafeType.GetMethod("Unbox", staticPub).MakeGenericMethod(structType); object valueRef = unboxMethod.Invoke(null, new[] { value }); // 第二步:反射调用CreateSpan创建对应结构体类型的Span MethodInfo createSpan = mmType.GetMethod("CreateSpan", staticPub).MakeGenericMethod(structType); object structSpan = createSpan.Invoke(null, new[] { valueRef, 1 }); // 第三步:反射调用AsBytes将结构体Span转为字节Span MethodInfo asBytes = mmType.GetMethod("AsBytes", staticPub).MakeGenericMethod(structType); object byteSpan = asBytes.Invoke(null, new[] { structSpan }); // 第四步:将栈上的Span<byte>转为堆上的字节数组返回 MethodInfo toArray = mmType.GetMethod("ToArray", staticPub).MakeGenericMethod(typeof(byte)); return (byte[])toArray.Invoke(null, new[] { byteSpan }); }
效果验证
传入你定义的Test结构体实例new Test { Code1 = 1, Code2 = 2 }调用上述方法,会返回长度为8的字节数组,内容为[1,0,0,0,2,0,0,0],和你直接写强类型代码得到的结果完全一致。
内容的提问来源于stack exchange,提问作者Alfonso-Rachid Huilda
相关产品推荐
相关产品推荐

