关键字`in, out, ref` vs 属性`[In], [Out], [In, Out]`

Keywords `in, out, ref` vs Attributes `[In], [Out], [In, Out]`

我知道第一组关键字in, out, ref可以在所有C#函数中使用,第二组属性[In], [Out], [In, Out]是给marshaller用的。

我不确定它们在本机代码的函数声明中使用时是否表示相同的意思。比如下面两个声明是等价的吗?

[DllImport("xxx.dll")]
void FillArray1(ref int[] arr, in int length);

[DllImport("xxx.dll")]
void FillArray2([In, Out] int[] arr, [In] int length);

是否存在两组不等价的情况?

它们不等价。

对于ref int[] arr,将自动应用默认的[In, Out]属性,但它仍然与[In, Out] int[] arr不同。

ref int[] arr 是双重间接寻址(通过引用传递的引用类型)。如果本机端定义如下:int32_t** arr,请使用此选项。这不仅允许替换元素,还允许替换整个数组实例。

另一方面,[In, Out] int[] arr 是按值传递的简单引用。如果本机端也使用单一间接寻址,例如,请使用此选项。 int32_t* arr。通常在 C# 中,如果按值(这是引用类型)传递数组,被调用的方法可以替换元素,这将从调用方反映出来。但是,P/Invoke 编组工作有点 differently:

By default, reference types (classes, arrays, strings, and interfaces) passed by value are marshaled as In parameters for performance reasons. You do not see changes to these types unless you apply InAttribute and OutAttribute (or just OutAttribute) to the method parameter.

因此无论指定Out属性如何,本机端都会得到一个正确的指针。封送拆收器需要在此处指定 [Out],因此它不会忽略与托管内存的 copy-back 会话。

类似地,in int length 将传递对整数的引用,与 [In] int length 不同,后者仅按值传递参数。 [In] 可以省略,因为这是本例中的默认编组行为。