ここでは構造体のサイズの取得を取得する方法としてMarshal.SizeOfメソッド、sizeof演算子(C#)、Len関数(VB)について解説します。 これらは多くの場合同じ結果を返しますが、いくつかの相違点があります。 (§.Marshal.SizeOfとsizeofの違い)
また、StructLayoutAttribute属性によってアラインメント(Pack)とサイズ(Size)を指定した場合の動作についても解説します。
Marshal.SizeOfメソッド
整数型など型のサイズが定義されている構造体の場合、Marshal.SizeOfメソッドを使うことにより型のサイズ(バイト数)を取得することができます。 引数に値やオブジェクトを直接指定するか、typeof
/GetType
で取得した型情報を渡すことにより、その型のサイズを取得することが出来ます。
IntPtr
は実行環境によってサイズが変わります。 32ビット環境では4
、64ビット環境では8
が返されます。 上記の実行結果は32ビット環境でのものです。 IntrPtr
のサイズはIntPtr.Sizeプロパティによっても取得することができます。
この値を利用して実行環境が32ビットか64ビットか判別することもできます。 (ランタイム・システム・プラットフォームの情報 §.32ビット環境・64ビット環境)
基本型のサイズの一覧については型の種類・サイズ・精度・値域 §.型のサイズ・精度と値域を参照してください。
どのような型でもMarshal.SizeOfでサイズを取得出来るわけではありません。 例えばDateTime
などアンマネージコードに渡す際のマーシャリング方法が定義されていない型の場合はサイズを取得できません。 型のサイズが取得できない場合、Marshal.SizeOfメソッドはArgumentExceptionをスローします。
DateTime
のほか、DateTimeOffset
、string
やobject
などの参照型のもサイズが取得できません。
構造体の場合では、StructLayout属性でLayoutKind.Sequential
またはLayoutKind.Explicit
が指定されていればMarshal.SizeOfメソッドで構造体のサイズを取得することができます。
StructLayout属性について詳しくはフィールドのレイアウト・オフセットを参照してください。
.NET Framework 4.5.1以降ではMarshal.SizeOf<T>メソッドが用意されています。 typeof
/GetType
で型情報を指定する代わりに型パラメータT
を指定するだけで型のサイズが取得できるようになります。
sizeof演算子 (C#)
C#ではsizeof演算子によっても型のサイズを取得することができます。 sizeof
演算子はint
やbool
などの組み込み型に対して使用することができ、定義済みのサイズを返します。 また、参照型のメンバを含まない構造体に対して使用することもできますが、その場合はunsafe
コンテキスト内で使用する必要があります。
Marshal.SizeOfとsizeofの違い
型によってはMarshal.SizeOf
メソッドとsizeof
演算子で異なる結果となる場合があります。 例えばbool型では次のように異なる結果を返します。
これは、Marshal.SizeOf
メソッドがアンマネージAPI呼び出し時にマーシャリングされる際のサイズを返すのに対し、sizeof
演算子はマネージドコード上でのサイズを返すという違いがあるためです。
マネージドコードではbool
型は1バイトとされているためsizeof(bool)
は1
を返します。 一方、アンマネージAPI呼び出しに際してbool
型はWin32 BOOL
型にマーシャリングされます。 このBOOL
型はtypedef int BOOL;
(=4バイト符号付き整数)とされているため、Marshal.SizeOf(typeof(bool))
は4
を返します。
- マーシャリングに関するドキュメント
Len関数 (VB)
Visual BasicではLen関数によっても型のサイズを取得することができます。 Len
関数はMarshal.SizeOfメソッドやsizeof演算子とは異なり、型ではなく具体的な値または変数(オブジェクト)を指定することにより、そのサイズを返します。
Len
関数が返すサイズは、FilePut関数(VB6以前のPutステートメントに相当)で書き込まれる際のサイズとされています。 具体的には、Boolean
が2、DateTime
が8とされているほか、基本型(Char
を含む数値型)であればそのサイズが返されます。 (型の種類・サイズ・精度・値域 §.型のサイズ・精度と値域) ただし、DateTime
のサイズは8とされている一方で、DateTimeOffset
は0となるようです。
文字列あるいはChar配列の場合はその長さ・Lengthプロパティの値が返されます。 ただし、VBFixedString属性によって固定長の文字列として定義されているフィールドの場合は、その長さが返されます。 Nothing
の場合は例外とはならず0が返されます。 構造体の場合、基本的には構成される基本型の組み合わせに基づいて計算されますが、具体的な動作についてはFilePut関数を参照してください。
アラインメント(Pack)・サイズ(Size)
Marshal.SizeOf
メソッドおよびsizeof
演算子は構造体のアラインメントに応じた結果を返します。 一方、Len
関数ではアラインメントは無視され、フィールドのサイズのみに基づいた結果を返します。 次の例ではStructLayout属性
のPack
パラメータを指定して構造体のアラインメントを変更しています。
Size
パラメータを指定して構造体のサイズを指定した場合も同様で、Marshal.SizeOf
メソッドおよびsizeof
演算子は構造体のサイズに応じた結果を返します。 一方、Len
関数ではサイズは無視され、フィールドのサイズのみに基づいた結果を返します。