我使用的函数SendBuffer(int size, IntPtr pointer)
以及IntPtr
作为参数。
var list = new List<float>{3, 2, 1};
IntPtr ptr = list.getPointerToInternalArray();
SendBuffer(ptr, list.Count);
如何从IntPtr
(和/或List<T>
)中存储的数组中获取T[]
?
答案 0 :(得分:4)
如果这是对非托管代码的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();
}
答案 1 :(得分:2)
不能保证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();
(或者通过更多错误处理来查看taffer的答案)。
在这两种情况下,您需要在返回之前完成该值,因此您不能使用任何一种方法将IntPtr
作为返回值获取到数组。这样做可以最大限度地减少指针用于恶意的机会。
答案 2 :(得分:2)
每帧发送一次数组,它很大
在这种情况下,可能需要访问List
使用的内部后备阵列。面对未来的.NET版本,这是一个破解和脆弱。那就是说.NET使用了一个非常高的兼容性栏,它们可能不会改变这种核心类型的字段名称。此外,出于性能原因,几乎可以保证List
将始终对其项目使用单个后备阵列。因此,虽然这是一种高风险的技术,但在这里可能是必要的。
或者,更好的是,编写您自己控制的List,并且可以从中获取数组。 (因为你似乎关注perf我不知道你为什么要使用List<float>
因为访问项目比普通数组慢。)
获取阵列,然后使用fixed(float* ptr = array) SendBuffer(ptr, length)
将其固定并传递,而不复制内存。
这里不需要使用笨拙和慢GCHandle
类型。使用fixed
进行固定会使用IL功能来实现超快速。应该接近零成本。