4

Microsoft recommends in Write safe and efficient C# code:

Apply the in modifier to readonly struct parameters larger than System.IntPtr.Size

Is there a simple way to check the managed memory size of a ref struct like ReadOnlySpan<byte>?

The following methods don't work:

// CS0208 Cannot get the size of a managed type
unsafe { size = sizeof(ReadOnlySpan<byte>); }

// CS0306 The type 'ReadOnlySpan<byte>' may not be used as a type argument
size = Marshal.SizeOf(default(ReadOnlySpan<byte>));

// ArgumentException "must not be a generic type definition"
size = Marshal.SizeOf(typeof(ReadOnlySpan<byte>));
  • I know it's possible to estimate the size by recursively reflecting all fields and StructLayout attributes, by which I think that `ReadOnlySpan` in .NET Core 3.1 uses 16 bytes in managed memory. I don't find that method simple, though of course it's better than nothing. – Joel V. Earnest-DeYoung Apr 19 '20 at 13:45
  • 1
    Does [this approach](https://stackoverflow.com/a/18167584/653473) work? It's calling `Marshal.SizeOf` on a `default(T)` "instance", where `T` would be `ReadOnlySpan`. – dialer Apr 19 '20 at 16:16
  • @dialer It doesn't compile because ref struct types cannot be used as type arguments for generic methods (like `Marshal.SizeOf(T)`). Thanks, that's a really obvious case that I forgot to include in the question, and have now added. – Joel V. Earnest-DeYoung Apr 19 '20 at 16:34
  • @dialer Though `Marshal.SizeOf(default(ReadOnlySpan))` doesn't work, the approach you link to with `ILGenerator.Emit(OpCodes.SizeOf`... **does** work. Would you write it as an answer? – Joel V. Earnest-DeYoung Apr 19 '20 at 17:46
  • Just leave it; you did the work after all /shrug – dialer Apr 20 '20 at 16:59

1 Answers1

5

From this answer, the IL sizeof instruction will return the managed size, even for types that C# sizeof doesn't accept, including generic ref struct types like ReadOnlySpan<byte> and Span<byte>.

Type type = typeof(ReadOnlySpan<byte>);

var dm = new DynamicMethod("$", typeof(int), Type.EmptyTypes);
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Sizeof, type);
il.Emit(OpCodes.Ret);

int size = (int)dm.Invoke(null, null);