如何在List <t>中获取数组的IntPtr?

时间:2016-01-23 10:15:18

标签: c# marshalling intptr

我使用的函数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[]

3 个答案:

答案 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功能来实现超快速。应该接近零成本。