public static class Marshal{ public static int SizeOf(object structure); public static int SizeOf(T structure); public static int SizeOf(Type t); public static int SizeOf()}
object value = default(Foobar);Debug.Assert(Marshal.SizeOf() == 16);Debug.Assert(Marshal.SizeOf(value) == 16);Debug.Assert(Marshal.SizeOf(typeof(Foobar)) == 16);Debug.Assert(Marshal.SizeOf(typeof(Foobar)) == 16);public struct Foobar{ public object Foo; public object Bar;}
由于如下这个Foobar被定义成了类,所以针对两个SizeOf方法的调用都会抛出ArgumentException异常,并提示:Type "Foobar" cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
Marshal.SizeOf();Marshal.SizeOf(new Foobar());public class Foobar{ public object Foo; public object Bar;}
public static class Unsafe{ public static int SizeOf();}
Debug.Assert( Unsafe.SizeOf() == 16);Debug.Assert( Unsafe.SizeOf() == 8);public struct FoobarStructure{ public long Foo; public long Bar;}public class FoobarClass{ public long Foo; public long Bar;}
上面我们介绍sizeof操作符和静态类型Marshal/Unsafe提供的SizeOf方法均不能真正解决实例占用字节长度的计算。就我目前的了解,这个问题在单纯的C#领域都无法解决,但IL层面提供的Ldflda指令可以帮助我们解决这个问题。顾名思义,Ldflda表示Load Field Address,它可以帮助我们得到实例某个字段的地址。由于这个IL指令在C#中没有对应的API,所以我们只有采用如下的形式采用IL Emit的来使用它。
public class SizeCalculator{ private static readonly ConcurrentDictionary _sizes = new(); public static readonly SizeCalculator Instance = new(); public int SizeOf(Type type, Func? instanceAccessor = null) { if (_sizes.TryGetValue(type, out var size)) return size; if (type.IsValueType) return _sizes.GetOrAdd(type, CalculateValueTypeInstance); object? instance; try { instance = instanceAccessor?.Invoke() ?? Activator.CreateInstance(type); } catch { throw new InvalidOperationException("The delegate to get instance must be specified."); } return _sizes.GetOrAdd(type, type => CalculateReferenceTypeInstance(type, instance)); } public int SizeOf(Func? instanceAccessor = null) { if (instanceAccessor is null) return SizeOf(typeof(T)); Func accessor = () => instanceAccessor(); return SizeOf(typeof(T), accessor); }}
Debug.Assert( SizeCalculator.Instance.SizeOf() == 16);Debug.Assert( SizeCalculator.Instance.SizeOf() == 32);public struct FoobarStructure{ public byte Foo; public long Bar;}public class FoobarClass{ public byte Foo; public long Bar;}