从.NET到C++的结构体数组编组:它何时进行复制?

9
考虑一个像System.Drawing.Point这样的结构体 - 其中有LayoutKind.Sequential,并且仅包含原始成员。我有一个C#结构体数组。 我通过P/Invoke将其传递给(非托管的)C++函数。在C++侧,有相应的结构定义(例如struct Point { int x,y; };)。该函数需要一个Point*参数。 我的问题是,在什么情况下CLR会复制数据,在什么情况下只需固定它?变量包括:
- 数组类型:一维或矩形 - C#函数定义 - 使用Point*还是Point[]/Point[,] - 使用fixed(Point* pointer = array)还是不使用
我想避免复制,因为它很慢。

它没有记录,简单的数组只是被固定了。如果有疑问,请使用调试器,在C#中键入*&array以获取数组地址。并与您在本机代码中获得的指针值进行比较。在32位模式下为+8。 - Hans Passant
2个回答

2
根据我阅读的MSDN,我对固定引脚不太熟悉,但是您的结构体应该被固定引脚而不是复制。相关参考资料:
  1. 使用PInvoke编组数据
  2. 复制和固定引脚
  3. 可镜像和不可镜像类型
  4. 值类型的默认编组
从#2中得知:

格式化的可镜像类在托管和非托管内存中具有固定的布局(格式化)和公共数据表示。当这些类型需要编组时,指向堆中对象的指针直接传递给被调用方。

并且您的Point结构在#3中作为示例给出,因此它符合可镜像类型的要求。
我注意到在这里固定对象会有一些开销(链接),但是那是针对fixed byte[]的,可能与您的Point[]不同。

0

结构体按值引用,类按引用引用。

这意味着,始终如果你有一个结构体并赋值,其值会被复制,就像使用 int a = b; a 将具有 b 的值而不是指向 b 的指针,因此如果更改 a 的值,则不会更新 b。

如果你有一个数组,内部存储默认值的向量,对于类则存储指针,而对于结构体则存储结构体的默认值。请注意,如果结构体包含引用类型(类)成员,则存储一个指向 null 的指针。

假设你有

Point p = new Point(0, 1);
Point[] pa = new Point[10];
pa[0] = p;
++p.X;

由于 Point 是一个结构体(值类型),当您打印这些值时

p: {1, 1}
pa[0]: {0, 1}
pa[1-9]: {0, 0}

对于 C++,您可以使用 foo(Point *pa) 或 foo(Point[] pa) 来获得相同的结果;在 C# 中,使用单维数组 foo(Point[] pa)。对于矩形,C++ 中使用 foo(Point **pa),而在 C# 中使用 foo(Point[][])。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接