托管结构的大小

.NET 4.0 Framework引入了用于读取和写入内存映射文件的类 。 这些类以读取和编写结构的方法为中心。 这些不是编组的,而是以在托管内存中布局的forms从文件复制到文件。

假设我想使用以下方法将两个结构顺序写入内存映射文件:

[StructLayout(LayoutKind.Sequential, Pack = 1)] struct Foo { public char C; public bool B; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct Bar { } static void Write(T1 item1, T2 item2) where T1 : struct where T2 : struct { using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 32)) using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor()) { accessor.Write(0L, ref item1); // <-- (1) accessor.Write(??, ref item2); // <-- (2) } } static void Main() { Foo foo = new Foo { C = 'α', B = true }; Bar bar = new Bar { }; Write(foo, bar); } 

如何获得(1)中写入的字节数,以便我可以在(2)中相邻写入下一个值?

注意:示例中的字节数为3(= 2 + 1),而不是Marshal.SizeOf返回的5(= 1 + 4)。

注2: sizeof无法确定generics类型参数的大小。

似乎没有文档/公共方法来访问MemoryMappedViewAccessor类使用的内部SizeOfType函数,因此获得这些结构大小的最实用方法是使用这样的reflection:

 static readonly Func SizeOfType = (Func)Delegate.CreateDelegate(typeof(Func), typeof(Marshal).GetMethod("SizeOfType", BindingFlags.NonPublic | BindingFlags.Static)); static void Write(T1 item1, T2 item2) where T1 : struct where T2 : struct { using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 32)) using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor()) { accessor.Write(0, ref item1); accessor.Write(SizeOfType(typeof(T1)), ref item2); } } 

您可以使用Emit访问Sizeof操作码并绕过编译器获取sizeof(T)的限制:

 var sizeOfMethod = new DynamicMethod( "GetManagedSizeImpl" , typeof(uint) , null , true); var genSizeOf = sizeOfMethod.GetILGenerator(); genSizeOf.Emit(OpCodes.Sizeof, typeof(T)); genSizeOf.Emit(OpCodes.Ret); var sizeOfFunction = (Func)sizeOfMethod.CreateDelegate(typeof(Func)); // ... int size = checked((int)sizeOfFunction()); 

1.一个答案建议使用内部函数Marshal.SizeOfType ,但这仅适用于不包含任何托管引用的结构。 在.NET 4.7上,它在传递引用( class )类型或包含嵌入引用的struct类型时抛出ArgumentException

2.另一个答案建议使用IL sizeof操作码。 这适用于所有struct值类型 – 包括具有嵌入引用的类型 – 但对于引用类型,它始终返回IntPtr.Size48 ),而不是(实例的)实际布局大小托管类。 根据您的具体情况,这可能是您想要的。 请注意,通过将包含单个嵌入引用(句柄)的结构的大小写与单个引用(句柄)本身混合,可以优雅地降低此结果。

调用sizeof IL指令的更简单方法是通过System.Runtime.CompilerServices.Unsafe包:

 int struct_layout_bytes = Unsafe.Sizeof(); 

3.如果由于某种原因确实需要托管类的(实例)的实际布局大小 ,则可以使用以下内容,这仅适用于引用类型,即typeof(T).IsValueTypefalse

 int class_layout_bytes = Marshal.ReadInt32(typeof(T).TypeHandle.Value, 4) 

因此,要从任何Type句柄获取任何引用或值类型的实例布局大小 (包括那些包含嵌入引用的实例布局大小) ,请合并方法#2和#3:

 int instance_layout_bytes = typeof(T).IsValueType ? Unsafe.Sizeof() : Marshal.ReadInt32(typeof(T).TypeHandle.Value, 4); 

related: 具有generics类型字段的struct的大小