To get the size of a data type in .Net you can use sizeof() or Marshal.SizeOf() .
I’ll briefly explain the difference between the two.
sizeof()
sizeof() (MSDN) can only be used on data types that have a known size at compile-time. It is compiled as a constant. If you attempt to use it on invalid data types you get a compiler error CS0233.
This means it has zero overhead during execution.
Example
1 |
sizeof(int) |
Compiles to IL code:
1 |
ldc.i4.4 |
Marshal.SizeOf()
System.Runtime.InteropServices.Marshal.SizeOf() (MSDN) however works on struct data types at runtime by measuring their size. This means both the overhead of calling the method, as well as extra work for boxing and measuring.
Example
1 |
Marshal.SizeOf<int>() |
Compiles to IL code:
1 |
call System.Runtime.InteropServices.Marshal.SizeOf<Int32> |
Peeking inside
If we look at the source code (referencesource)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
//==================================================================== // SizeOf() //==================================================================== [ResourceExposure(ResourceScope.None)] [System.Runtime.InteropServices.ComVisible(true)] public static int SizeOf(Object structure) { if (structure == null) throw new ArgumentNullException("structure"); // we never had a check for generics here Contract.EndContractBlock(); return SizeOfHelper(structure.GetType(), true); } public static int SizeOf<T>(T structure) { return SizeOf((object)structure); } [ResourceExposure(ResourceScope.None)] [Pure] public static int SizeOf(Type t) { if (t == null) throw new ArgumentNullException("t"); if (!(t is RuntimeType)) throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"), "t"); if (t.IsGenericType) throw new ArgumentException(Environment.GetResourceString("Argument_NeedNonGenericType"), "t"); Contract.EndContractBlock(); return SizeOfHelper(t, true); } public static int SizeOf<T>() { return SizeOf(typeof(T)); } [ResourceExposure(ResourceScope.None)] [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern int SizeOfHelper(Type t, bool throwIfNotMarshalable); |
From this we can see that the struct will be measured by external API call. If Type is provided, extra checks are made. If object is provided as parameter boxing from struct to object occurs.
Caveats
In two cases Marshal.SizeOf<T>() will differ from sizeof(T) .
1 2 3 4 |
Marshal.SizeOf<Boolean>(): 4 sizeof(Boolean): 1 Marshal.SizeOf<Char>(): 1 sizeof(Char): 2 |
This has to do with how .Net marshals data types to unmanaged code.
Managed data type Char (2 bytes) is identified as Windows type SBYTE (1 byte), and managed data type Boolean (1 byte) is identified as windows type BOOL (4 bytes).
If the you point to a struct in another assembly, the IL of sizeof would be similar to this ‘IL_0000: sizeof SizeTesting.Test’.
If you then change the library dll, the value also gets updated. So in this case, it would happen in runtime and not in compile time.