C#的数组互操作封送行为似乎与文档不一致

问题描述:

我目前正在为OpenGL编写一个瘦C#绑定。我刚刚实现了OpenGL GenVertexArrays函数,它具有以下签名: OpenGL Documentation on glGenVertexArraysC#的数组互操作封送行为似乎与文档不一致

本质上,你传递一个数组来存储由OpenGL创建的顶点数组的生成对象值。

为了创建绑定,我使用委托作为glGenVertexArrays是一个OpenGL扩展函数,所以我必须使用wglGetProcAddress动态加载它。我在C#中定义的委托签名看起来是这样的:

[SuppressUnmanagedCodeSecurity] 
[UnmanagedFunctionPointer(CallingConvention.StdCall)] 
private delegate void glGenVertexArrays(uint amount, uint[] array); 

函数指针被检索并转换为使用Marshal.GetDelegateForFunctionPointer此委托,像这样:

IntPtr proc = wglGetProcAddress(name); 

del = Marshal.GetDelegateForFunctionPointer(proc, delegateType); 

不管怎么说,这里是让我困扰:

在任何正式文件,我可以找到关于引用类型的默认编组行为(包括阵列),是这样的:

默认情况下,按值传递的引用类型(类,数组,字符串和接口) 将被编组为In参数,以获得性能 的原因。除非将 InAttribute和OutAttribute(或OutAttribute)应用于方法 参数,否则不会看到对这些类型的更改。

这是从这个MSDN网页采取:MSDN page on directional attributes

但是,从我的委托签名,将[IN]和[OUT]定向属性还没有被无符号整数数组上使用的可见,这意味着当我调用这个函数时,我实际上不应该能够看到OpenGL应该存储在其中的生成对象值。除了,我是。使用这个签名,我可以在运行调试器时以下结果:

Debugging Results is positive

可以看出,呼叫万万没有影响阵列,尽管我并没有明确使用[OUT]属性。根据我的理解,这不是我期待的结果。

有没有人知道背后的原因?我知道这可能看起来并不重要,但我很好奇,为什么这似乎打破了Microsoft描述的默认编组行为。与纯粹的平台调用原型相比,调用委托时是否存在一些幕后的内容?还是我误解了文档?

[编辑]

对于任何好奇,如随后调用上的静态“GL”类中定义的代表,并且是公众方法:

public static void GenVertexArrays(uint amount, uint[] array) 
{ 
    InvokeExtensionFunction<glGenVertexArrays>()(amount, array); 
} 

它不上提文档页面您链接,但another topic专用于阵列的编组,在那里说:

有了穿针优化,一个blittable阵列可以表现为一个输入/输出操作参数与同一个房间中的物体进行交互时。

这两个条件都符合你的情况:uint数组是blittable,并且没有机器到机器的编组。声明它[Out]仍然是一个好主意,所以你的意图被记录在代码中。

+0

谢谢! :)非常有帮助的答案! – CodingBeagle

该文档在一般情况下是正确的。但uint有点特别,它是一个blittable类型。一个昂贵的词,这意味着pinvoke编组无需做任何特殊的转换数组元素值。 C#中的uint与C中的unsigned int类型完全相同。根本不是巧合,它是处理器本身可以处理的类型。

所以编组人员可以简单地钉住数组并传递指向第一个数组元素的指针作为第二个参数。非常快,总是你想要的。该函数直接写入托管数组,因此不需要将值复制回来。有点危险,你永远不会想要说谎amount的论点,GC堆腐败是一个非常丑陋的诊断错误。

简单值类型的大多数简单值类型和结构都是blittable。 bool是一个显着的例外。否则,即使没有必要,您也不必为使用[Out]而感到抱歉。编组在这里简单地忽略它。

+0

非常感谢你的解释!我接受了另一个答案,因为它看起来是在你的答案之前发布的,并且你的答案都非常有价值,所以很难决定哪一个答案被接受:)尽管你的输入非常感谢! 干杯! – CodingBeagle