LayoutKind.Sequential
blittable 型で使用する場合、これは不要です
すべてのフィールドが blittable である限り、メモリ内の構造体フィールドの順序を調べるために、リフレクションやその他のメカニズムを使用する必要はありません。
with で宣言された構造体の blittable フィールドはLayoutKind.Sequential
、フィールドが宣言された順序でメモリに格納されます。というLayoutKind.Sequential
ことです!
このドキュメントから:
blittable 型の場合、LayoutKind.Sequential はマネージド メモリ内のレイアウトとアンマネージド メモリ内のレイアウトの両方を制御します。blittable でない型の場合、クラスまたは構造体がアンマネージ コードにマーシャリングされるときにレイアウトを制御しますが、マネージ メモリ内のレイアウトは制御しません。
これは、各フィールドが使用しているパディングの量を示していないことに注意してください。それを見つけるには、以下を参照してください。
を使用する場合のフィールドの順序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
。また、フィールドの名前を事前に知る必要もありません。上で述べたように、メモリ内のフィールドの順序を決定する必要はありませんが、これは、使用されているパディングの量を調べる必要がある場合に役立ちます。