The .NET 4.0 Framework introduces classes for reading and writing memory mapped files. The classes are centred around methods for reading and writing structures. These are not marshalled but copied from and to the file in the form in which they are laid out in managed memory.
Let's say I want to write two structures sequentially to a memory mapped file using these methods:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Foo
{
public char C;
public bool B;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bar
{
}
static void Write<T1, T2>(T1 item1, T2 item2)
where T1 : struct
where T2 : struct
{
using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 32))
using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor())
{
accessor.Write<T1>(0L, ref item1); // <-- (1)
accessor.Write<T2>(??, ref item2); // <-- (2)
}
}
static void Main()
{
Foo foo = new Foo { C = 'α', B = true };
Bar bar = new Bar { };
Write(foo, bar);
}
How would I get the number of bytes written in (1) so I can write the next value adjacently in (2)?
Note: The number of bytes in the example is 3(=2+1), not 5(=1+4) as returned by Marshal.SizeOf.
Note 2: sizeof
cannot determine the size of generic type parameters.
1. One answer on this page proposes using the internal function
Marshal.SizeOfType
, but this only works for structs which do not contain any managed references. On .NET 4.7 it throws anArgumentException
when passed a reference (class
) type, or astruct
type which contains embedded references.2. Another answer here suggests using the IL
sizeof
opcode. This works correctly for allstruct
value types--including generics and those with embedded references--but for reference types it always returnsIntPtr.Size
(i.e., the value4
or8
), as opposed to the actual layout size of (an instance of) the managed class. This may be what you want, depending on your situation. Note that this sort of result gracefully degrades by conflating the case of a struct containing a single embedded reference (handle) with a single reference (handle) itself.A simpler way to invoke the
sizeof
IL instruction is via the System.Runtime.CompilerServices.Unsafe package:3. If you truly need the actual layout size of (an instance of) a managed class for some reason, then you're probably doing something wrong, but you can obtain it via the following, which only works for reference types, that is, when
typeof(T).IsValueType
isfalse
.4. Therefore--and still with the preceding caveat--to get the instance layout size for any reference- or value-type--including those containing embedded references--from its
Type
handle, combine methods #2 and #3:related: Size of struct with generic type fields
It seems there is no documented/public way to access the internal
SizeOfType
function used by theMemoryMappedViewAccessor
class, so the most practical way of getting the size of those structures would be to use reflection like this:You can use Emit to access the Sizeof opcode and bypass compiler's restriction on getting sizeof(T):