C# 如何获取列表中数组的IntPtr<;T>;?
我正在使用一个库,它有一个函数C# 如何获取列表中数组的IntPtr<;T>;?,c#,marshalling,intptr,C#,Marshalling,Intptr,我正在使用一个库,它有一个函数SendBuffer(int size,IntPtr指针),其中IntPtr作为参数 var list = new List<float>{3, 2, 1}; IntPtr ptr = list.getPointerToInternalArray(); SendBuffer(ptr, list.Count); var list=新列表{3,2,1}; IntPtr ptr=list.getpointertointernarray(); SendB
SendBuffer(int size,IntPtr指针)
,其中IntPtr
作为参数
var list = new List<float>{3, 2, 1};
IntPtr ptr = list.getPointerToInternalArray();
SendBuffer(ptr, list.Count);
var list=新列表{3,2,1};
IntPtr ptr=list.getpointertointernarray();
SendBuffer(ptr,list.Count);
如何从存储在
列表(和/或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();
}
如果这是对非托管代码的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();
}
不能保证列表的内部表示形式将是单个数组。。。事实上,很可能不是这样。因此,您需要使用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
作为返回值发送到数组。这样做可以最大限度地减少指针被用于邪恶的机会。不能保证列表的内部表示将是单个数组。。。事实上,很可能不是这样。因此,您需要使用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
作为返回值发送到数组。这样做可以最大限度地减少指针用于邪恶的机会
数组每帧发送一次,而且很大
在这种情况下,可以保证访问List
使用的内部支持阵列。面对未来的.NET版本,这是一种攻击和脆弱。也就是说.NET使用了一个非常高的兼容性条,他们可能不会更改这样一个核心类型中的字段名。此外,出于性能原因,几乎可以保证List
始终为其项使用单个后备数组。因此,尽管这是一种高风险的技术,但在这里可能是值得的
或者,更好的做法是,编写自己的列表,由您控制并从中获取数组。(由于您似乎关心性能,我想知道您为什么要使用List
,因为与普通数组相比,访问项的速度较慢。)
获取数组,然后使用fixed(float*ptr=array)SendBuffer(ptr,length)
将其固定并传递,而不复制内存
这里不需要使用笨拙且缓慢的GCHandle
类型。使用固定的钉扎
使用IL功能使其超快速。成本应该接近于零
数组每帧发送一次,而且很大
在这种情况下,可以保证访问List
使用的内部支持阵列。面对未来的.NET版本,这是一种攻击和脆弱。也就是说.NET使用了一个非常高的兼容性条,他们可能不会更改这样一个核心类型中的字段名。此外,出于性能原因,几乎可以保证List
始终为其项使用单个后备数组。因此,尽管这是一种高风险的技术,但在这里可能是值得的
或者,更好的做法是,编写自己的列表,由您控制并从中获取数组。(由于您似乎关心性能,我想知道您为什么要使用List
,因为与普通数组相比,访问项的速度较慢。)
获取数组,然后使用fixed(float*ptr=array)SendBuffer(ptr,length)
将其固定并传递,而不复制内存
这里不需要使用笨拙且缓慢的GCHandle
类型。使用固定的钉扎
使用IL功能使其超快速。成本应该接近于零。调用ToArray,或者这对您来说太慢了?@HansPassant:如果他在托管端创建数组,那么GC可能会重新定位数组,从而使地址无效。ToArray()分配内存并制作副本,这是性能关键部分。数组每帧发送一次,而且非常大。调用ToArray,或者对你来说太慢了?@HansPassant:如果他在托管端创建数组,那么GC可能会重新定位数组,从而使地址无效。ToArray()分配内存并制作副本,这是性能关键部分。数组每帧发送一次,而且它很大。使用fixed
会更优雅:fixed(float*p=array)Send(p,array.length)
@firda:当然,但前提是允许您在应用程序中使用不安全的代码project@taffer为什么不允许他使用不安全的代码?它与钉住的句柄和PInvoke具有相同的安全含义。这看起来很棒,有没有可能为List使用类似的机制?据我所知,List在内部使用数组。List.ToArray()
?;)使用fixed
会更优雅:fixed(float*p=array)Send(p,array.length)
@firda:当然,但前提是允许您在应用程序中使用不安全的代码project@taffer他为什么不呢