Computing size is something that is fraught with problems because you need to know what is meaningful in the context you are using it. I'd assume there is a good reason for Marshal.SizeOf
to throw when the argument is a generic struct, but I don't know what it is.
With that caveat, this code seems to work and gives similar results to Marshal.SizeOf
for non-generic structs. It generates a new dynamic method that gets the size via the sizeof IL opcode for the type. It then caches the result (since generating a dynamic method is some what expensive) for future use.
public class A { int x,y,z; }
public struct B { int x,y,z,w,a,b; }
public struct C<T> { Guid g; T b,c,d,e,f; }
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine(IntPtr.Size); // on x86 == 4
Console.WriteLine(SizeHelper.SizeOf(typeof(C<double>))); // prints 56 on x86
Console.WriteLine(SizeHelper.SizeOf(typeof(C<int>))); // prints 36 on x86
}
}
static class SizeHelper
{
private static Dictionary<Type, int> sizes = new Dictionary<Type, int>();
public static int SizeOf(Type type)
{
int size;
if (sizes.TryGetValue(type, out size))
{
return size;
}
size = SizeOfType(type);
sizes.Add(type, size);
return size;
}
private static int SizeOfType(Type type)
{
var dm = new DynamicMethod("SizeOfType", typeof(int), new Type[] { });
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Sizeof, type);
il.Emit(OpCodes.Ret);
return (int)dm.Invoke(null, null);
}
}
Edit
As far as I can tell there is no way to make non-generic delegate that you can cache. The SizeOf
opcode requires a metadata token. It does not take a value from the evaluation stack.
Actually the code below works as well. I'm not sure why Marshal.SizeOf(Type)
throws an argument exception when the type is generic structure but Marshal.SizeOf(Object)
does not.
public static int SizeOf<T>() where T : struct
{
return Marshal.SizeOf(default(T));
}