I know that the first set of keywords in, out, ref
can be used in all C# functions, and the second set of attributes [In], [Out], [In, Out]
is for marshaller.
I am not sure if they mean the same thing when used in function declaration of native code. For example, are the following two declarations equivalent?
[DllImport("xxx.dll")]
void FillArray1(ref int[] arr, in int length);
[DllImport("xxx.dll")]
void FillArray2([In, Out] int[] arr, [In] int length);
Are there any case that the two sets are not equivalent?
They are not equivalent.
For the ref int[] arr
the default [In, Out]
attributes will be applied automatically, but it is still not the same as [In, Out] int[] arr
.
ref int[] arr
is a double indirection (a reference type passed by reference). Use this if the native side is defined something like this: int32_t** arr
. This allows to replace not just the elements but the whole array instance as well.
On the other hand, [In, Out] int[] arr
is a simple reference passed by value. Use this if the native side also uses single indirection, eg. int32_t* arr
. Normally in C# if you pass an array by value (which is a reference type) the called method can replace the elements, which will be reflected from the caller side. However, P/Invoke marshaling works a bit 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 applyInAttribute
andOutAttribute
(or justOutAttribute
) to the method parameter.
So the native side gets a correct pointer regardless of specifying the Out
attribute. Specifying [Out]
here is needed for the marshaler so it will not omit the copy-back session to the managed memory.
Similarly, in int length
will pass a reference to an integer and is not the same as [In] int length
, which passes the parameter simply by value. The [In]
can be omitted as this is the default marshaling behavior in this case.