如果使用blittable类型和LayoutKind.Sequential
,则此操作是不必要的。
只要所有字段都是blittable类型,就无需使用反射或任何其他机制来查找结构体字段在内存中的顺序。
使用LayoutKind.Sequential
声明的结构体的blittable字段将按照声明字段的顺序存储在内存中。这就是LayoutKind.Sequential
的含义!
引用自此文档:
对于可按位复制类型,LayoutKind.Sequential控制托管内存中的排列方式和非托管内存中的排列方式。对于不可按位复制类型,它会控制类或结构体被编组到非托管代码时的排列方式,但不控制托管内存中的排列方式。
请注意,这并不告诉您每个字段使用了多少填充空间。为了找出这一点,请参阅下面的内容。
要确定使用LayoutKind.Auto
时的字段顺序,或使用任何布局时的字段偏移量
如果您愿意使用不安全代码并且不使用反射,那么找到结构体字段的偏移量是相当容易的。
您只需要获取结构体每个字段的地址,并计算其相对于结构体开始位置的偏移量。知道每个字段的偏移量后,您可以计算它们的顺序(以及它们之间的任何填充字节)。要计算用于最后一个字段(如果有)的填充字节,您还需要使用sizeof(StructType)
获得结构体的总大小。
以下示例适用于32位和64位。请注意,您无需使用fixed
关键字,因为该结构体已经由于存储在堆栈上而被修复(如果尝试使用fixed
,则会出现编译错误):
using System;
using System.Runtime.InteropServices;
namespace Demo
{
[StructLayout(LayoutKind.Auto, Pack = 1)]
public struct TestStruct
{
public int I;
public double D;
public short S;
public byte B;
public long L;
}
class Program
{
void run()
{
var t = new TestStruct();
unsafe
{
IntPtr p = new IntPtr(&t);
IntPtr pI = new IntPtr(&t.I);
IntPtr pD = new IntPtr(&t.D);
IntPtr pS = new IntPtr(&t.S);
IntPtr pB = new IntPtr(&t.B);
IntPtr pL = new IntPtr(&t.L);
Console.WriteLine("I offset = " + ptrDiff(p, pI));
Console.WriteLine("D offset = " + ptrDiff(p, pD));
Console.WriteLine("S offset = " + ptrDiff(p, pS));
Console.WriteLine("B offset = " + ptrDiff(p, pB));
Console.WriteLine("L offset = " + ptrDiff(p, pL));
Console.WriteLine("Total struct size = " + sizeof(TestStruct));
}
}
long ptrDiff(IntPtr p1, IntPtr p2)
{
return p2.ToInt64() - p1.ToInt64();
}
static void Main()
{
new Program().run();
}
}
}
使用 LayoutKind.Sequential
时确定字段偏移量
如果您的结构体使用 LayoutKind.Sequential
,则可以直接使用 Marshal.OffsetOf()
来获取偏移量,但是这在使用 LayoutKind.Auto
时无法实现:
foreach (var field in typeof(TestStruct).GetFields())
{
var offset = Marshal.OffsetOf(typeof (TestStruct), field.Name);
Console.WriteLine("Offset of " + field.Name + " = " + offset);
}
如果你使用LayoutKind.Sequential
,这显然是一种更好的方法,因为它不需要unsafe
代码,并且更短 - 而且你不需要预先知道字段的名称。正如我在上面所说的,没有必要确定内存中字段的顺序 - 但如果你需要找出使用了多少填充,这可能很有用。
LayoutKind.Sequential
只在结构体中只有 blittable 类型时控制托管表示。如果存在 unblittable 类型,则字段顺序由运行时控制。例如,请参见 https://dev59.com/dmzXa4cB1Zd3GeqPT3jA。 - GSerg