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他为什么不呢