How do the In and Out attributes work in .NET?
Solution 1:
This is a remarkably poorly documented feature of .NET Remoting. It doesn't have anything to do with whether your class is [Serializable] or derived from MarshalByRefObject. At issue here is how the argument is marshaled across the AppDomain boundary. The call itself is made under the hood by Remoting. Arrays do not automatically get marshaled back after the call, clearly a performance optimization. Only the [Out] attribute is required, [In] is implied. I could not find any relevant documentation about this in MSDN, just a blog post from somebody that ran into the same issue (scroll down to "Using OutAttribute in Remoting").
Some code to play with:
using System;
using System.Runtime.InteropServices;
class Program {
static void Main(string[] args) {
var ad = AppDomain.CreateDomain("second");
var t = (Test)ad.CreateInstanceAndUnwrap(typeof(Test).Assembly.FullName, typeof(Test).FullName);
var b = new byte[] { 1 };
t.Read(b);
System.Diagnostics.Debug.Assert(b[0] == 2);
}
}
class Test : MarshalByRefObject {
public void Read([Out]byte[] arg) {
arg[0] *= 2;
}
}
Solution 2:
To note, there has been additional documentation added since the original accepted answer was posted.
The definition of the directional attributes is:
- [In]: Data modified in the unmanaged code is not written back to managed code. All data passed by value is treated as [In] by default.
- [In/Out]: Data modified in the unmanaged code writes back to managed code. Data passed by reference (including by using the ref keyword) are treated as [In/Out] by default.
- [Out]: Parameters modified by the out C# keyword must as expected be passed back - and default to [Out]. Using [Out] explicitly will behave identically to [In/Out] ([In] is always assumed).
So the key uses of explicitly modifying these are:
- To instruct the program not to pass back modified data passed by reference - by using [In]
- To instruct the program to return modified data passed to the unmanaged code - by using [In/Out] or simply [Out] (they are equivalent).
The latter of these is particularly useful for reference types which pass by value by default, such as arrays of value types - e.g. byte[].