C#:将通用指针转换为数组

我想将一个byte*转换为byte* byte[] ,但我也希望有一个可重用的函数来执行此操作:

 public unsafe static T[] Create(T* ptr, int length) { T[] array = new T[length]; for (int i = 0; i < length; i++) array[i] = ptr[i]; return array; } 

不幸的是我收到编译器错误,因为T可能是“.NET托管类型”,我们无法指向那些 。 更令人沮丧的是,没有generics类型约束可以将T限制为“非托管类型”。 是否有内置的.NET函数来执行此操作? 有任何想法吗?

可以匹配您尝试执行的操作的方法是Marshal.Copy ,但它没有采用适当的参数来创建generics方法。

虽然不可能用可以描述可能性的通用约束编写generics方法,但不能使用“不安全”方式复制每种类型。 有一些例外; 课程就是其中之一。

这是一个示例代码:

  public unsafe static T[] Create(void* source, int length) { var type = typeof(T); var sizeInBytes = Marshal.SizeOf(typeof(T)); T[] output = new T[length]; if (type.IsPrimitive) { // Make sure the array won't be moved around by the GC var handle = GCHandle.Alloc(output, GCHandleType.Pinned); var destination = (byte*)handle.AddrOfPinnedObject().ToPointer(); var byteLength = length * sizeInBytes; // There are faster ways to do this, particularly by using wider types or by // handling special lengths. for (int i = 0; i < byteLength; i++) destination[i] = ((byte*)source)[i]; handle.Free(); } else if (type.IsValueType) { if (!type.IsLayoutSequential && !type.IsExplicitLayout) { throw new InvalidOperationException(string.Format("{0} does not define a StructLayout attribute", type)); } IntPtr sourcePtr = new IntPtr(source); for (int i = 0; i < length; i++) { IntPtr p = new IntPtr((byte*)source + i * sizeInBytes); output[i] = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(T)); } } else { throw new InvalidOperationException(string.Format("{0} is not supported", type)); } return output; } unsafe static void Main(string[] args) { var arrayDouble = Enumerable.Range(1, 1024) .Select(i => (double)i) .ToArray(); fixed (double* p = arrayDouble) { var array2 = Create(p, arrayDouble.Length); Assert.AreEqual(arrayDouble, array2); } var arrayPoint = Enumerable.Range(1, 1024) .Select(i => new Point(i, i * 2 + 1)) .ToArray(); fixed (Point* p = arrayPoint) { var array2 = Create(p, arrayPoint.Length); Assert.AreEqual(arrayPoint, array2); } } 

该方法可以是通用的,但它不能采用generics类型的指针。 这不是一个问题,因为指针协方差有所帮助,但这会产生阻止generics参数类型的隐式解析的不幸效果。 然后,您必须明确指定MakeArray。

我为结构添加了一个特例,最好有类型指定结构布局 。 在您的情况下,这可能不是问题,但如果指针数据来自本机C或C ++代码,则指定布局类型很重要(CLR可能会选择重新排序字段以获得更好的内存对齐)。

但是,如果指针完全来自托管代码生成的数据,那么您可以删除检查。

此外,如果性能是一个问题,那么复制数据的算法比逐字节更好。 (参见memcpy的无数实现以供参考)

似乎问题变成:如何将generics类型指定为简单类型。

 unsafe void Foo() : where T : struct { T* p; } 

给出错误:
不能获取地址,获取大小,或声明指向托管类型(’T’)的指针

这个怎么样?

 static unsafe T[] MakeArray(void* t, int length, int tSizeInBytes) where T:struct { T[] result = new T[length]; for (int i = 0; i < length; i++) { IntPtr p = new IntPtr((byte*)t + (i * tSizeInBytes)); result[i] = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(T)); } return result; } 

我们不能在这里使用sizeof(T),但是调用者可以做类似的事情

 byte[] b = MakeArray(pBytes, lenBytes, sizeof(byte)); 

我不知道如果以下内容可行,但它可能(至少它编译:):

 public unsafe static T[] Create(void* ptr, int length) where T : struct { T[] array = new T[length]; for (int i = 0; i < length; i++) { array[i] = (T)Marshal.PtrToStructure(new IntPtr(ptr), typeof(T)); } return array; } 

关键是使用Marshal.PtrToStructure转换为正确的类型。