如何在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

如何在List<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作为返回值。 这样做可以最大程度地减少该指针用于邪恶的机会。

相关文章: