如何在List中获取数组的IntPtr
本文关键字:获取 数组 IntPtr List | 更新日期: 2023-09-27 18:31:43
我正在使用一个库,该库具有以IntPtr
作为参数的函数SendBuffer(int size, IntPtr pointer)
。
var list = new List<float>{3, 2, 1};
IntPtr ptr = list.getPointerToInternalArray();
SendBuffer(ptr, list.Count);
如何从存储在List<T>
(和/或T[]
)中的数组中获取IntPtr
?
如果这是对非托管代码的 P/Invoke 调用,则应检索缓冲区的固定地址(以防止 GC 重新定位缓冲区)并将其传递给方法:
// use an array as a buffer
float[] buffer = new float[]{3, 2, 1};
// pin it to a fixed address:
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
// retrieve the address as a pointer and use it to call the native method
SendBuffer(handle.AddrOfPinnedObject(), buffer.Length);
}
finally
{
// free the handle so GC can collect the buffer again
handle.Free();
}
数组每帧发送一次,而且很大
在这种情况下,可能需要访问List
使用的内部后备阵列。面对未来的 .NET 版本,这是一个黑客和脆弱。也就是说,.NET 使用非常高的兼容性栏,他们可能不会更改此类核心类型的字段名称。此外,出于性能原因,几乎可以保证List
将始终对其项目使用单个后备阵列。因此,尽管这是一种高风险技术,但在这里可能是有道理的。
或者,更好的是,编写您自己的列表,您可以控制并从中获取数组。(由于您似乎关心性能,我想知道您为什么要使用List<float>
因为与普通数组相比,访问项目的速度更慢。
获取数组,然后使用 fixed(float* ptr = array) SendBuffer(ptr, length)
固定它并传递它而不复制内存。
这里没有必要使用笨拙而缓慢的GCHandle
类型。使用 fixed
进行固定使用 IL 功能来实现此速度超快。应该接近零成本。
不能保证List<T>
的内部表示将是单个数组...事实上,很可能不是。 因此,您需要使用 ToArray
创建一个本地数组副本才能使其正常工作。
一旦你有了,有几个选择。
首先,可以使用 fixed
关键字固定数组并获取指向它的指针:
T[] buffer = theList.ToArray();
unsafe
{
fixed (T* p = buffer)
{
IntPtr ptr = (IntPtr)p;
SomeFunction(ptr);
}
}
或者,您可以告诉垃圾回收器修复内存中的数据,直到完成操作,如下所示:
GCHandle pinned = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr ptr = pinned.AddrOfPinnedObject();
SomeFunction(ptr);
pinnedArray.Free();
(或者查看塔弗的答案,其中包含更多错误处理)。
在这两种情况下,您都需要在返回之前完成值,因此您不能使用任何一种方法来获取数组的IntPtr
作为返回值。 这样做可以最大程度地减少该指针用于邪恶的机会。