§1 Marshal.SizeOfメソッド

整数型など型のサイズが定義されている構造体の場合、Marshal.SizeOfメソッドを使うことにより型のサイズ(バイト数)を取得することができます。 引数に値やオブジェクトを直接指定するか、typeof/GetTypeで取得した型情報を渡すことにより、その型のサイズを取得することが出来ます。

using System;
using System.Runtime.InteropServices;

class Sample {
  static void Main()
  {
    Console.WriteLine("SizeOf({0}) = {1}", 16, Marshal.SizeOf(16)); // int
    Console.WriteLine("SizeOf({0}) = {1}", 3L, Marshal.SizeOf(3L)); // long
    Console.WriteLine("SizeOf({0}) = {1}", 1.414f, Marshal.SizeOf(1.414f)); // float
    Console.WriteLine("SizeOf({0}) = {1}", 3.14, Marshal.SizeOf(3.14)); // double
    Console.WriteLine();

    Console.WriteLine("SizeOf({0}) = {1}", typeof(int), Marshal.SizeOf(typeof(int)));
    Console.WriteLine("SizeOf({0}) = {1}", typeof(long), Marshal.SizeOf(typeof(long)));
    Console.WriteLine("SizeOf({0}) = {1}", typeof(float), Marshal.SizeOf(typeof(float)));
    Console.WriteLine("SizeOf({0}) = {1}", typeof(double), Marshal.SizeOf(typeof(double)));
    Console.WriteLine("SizeOf({0}) = {1}", typeof(IntPtr), Marshal.SizeOf(typeof(IntPtr)));
  }
}
実行結果
SizeOf(16) = 4
SizeOf(3) = 8
SizeOf(1.414) = 4
SizeOf(3.14) = 8

SizeOf(System.Int32) = 4
SizeOf(System.Int64) = 8
SizeOf(System.Single) = 4
SizeOf(System.Double) = 8
SizeOf(System.IntPtr) = 4

IntPtrは実行環境によってサイズが変わります。 32ビット環境では4、64ビット環境では8が返されます。 上記の実行結果は32ビット環境でのものです。 IntrPtrのサイズはIntPtr.Sizeプロパティによっても取得することができます。

この値を利用して実行環境が32ビットか64ビットか判別することもできます。 (ランタイム・システム・プラットフォームの情報 §.32ビット環境・64ビット環境)

どのような型でもMarshal.SizeOfでサイズを取得出来るわけではありません。 例えばDateTimeなどアンマネージコードに渡す際のマーシャリング方法が定義されていない型の場合はサイズを取得できません。 型のサイズが取得できない場合、Marshal.SizeOfメソッドはArgumentExceptionをスローします。

using System;
using System.Runtime.InteropServices;

class Sample {
  static void Main()
  {
    // DateTime型のサイズは取得できない
    Console.WriteLine("SizeOf({0}) = {1}", typeof(DateTime), Marshal.SizeOf(typeof(DateTime)));
  }
}
実行結果
ハンドルされていない例外: System.ArgumentException: 型 'System.DateTime' はアンマネージ構造体としてマーシャリングできません。有効なサイズ、またはオフセットの計算ができません。
   場所 System.Runtime.InteropServices.Marshal.SizeOfHelper(Type t, Boolean throwIfNotMarshalable)
   場所 System.Runtime.InteropServices.Marshal.SizeOf(Type t)
   場所 Sample.Main()

DateTimeのほか、DateTimeOffsetstringobjectなどの参照型のもサイズが取得できません。

構造体の場合では、StructLayout属性LayoutKind.SequentialまたはLayoutKind.Explicitが指定されていればMarshal.SizeOfメソッドで構造体のサイズを取得することができます。

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
struct Point {
  int X;
  int Y;
}

[StructLayout(LayoutKind.Explicit)]
struct ARGB {
  [FieldOffset(0)] uint Value;

  [FieldOffset(0)] byte B;
  [FieldOffset(1)] byte G;
  [FieldOffset(2)] byte R;
  [FieldOffset(3)] byte A;
}

class Sample {
  static void Main()
  {
    Console.WriteLine("SizeOf({0}) = {1}", typeof(Point), Marshal.SizeOf(typeof(Point)));
    Console.WriteLine("SizeOf({0}) = {1}", typeof(ARGB), Marshal.SizeOf(typeof(ARGB)));
  }
}
実行結果
SizeOf(Point) = 8
SizeOf(ARGB) = 4

StructLayout属性について詳しくはフィールドのレイアウト・オフセットを参照してください。

また、.NET Framework 4.5.1以降ではMarshal.SizeOf<T>メソッドが用意されています。 typeof/GetTypeで型情報を指定する代わりに型パラメータTを指定するだけで型のサイズが取得できるようになります。

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
struct Point {
  int X;
  int Y;
}

class Sample {
  static void Main()
  {
    Console.WriteLine("SizeOf<int> = {0}", Marshal.SizeOf<int>());
    Console.WriteLine("SizeOf<Point> = {0}", Marshal.SizeOf<Point>());
  }
}
実行結果
SizeOf<int> = 4
SizeOf<Point> = 8


§2 sizeof

C#ではsizeofによっても型のサイズを取得することができます。 sizeofintboolなどの組み込み型に対して使用することができ、定義済みのサイズを返します。 また、参照型のメンバを含まない構造体に対して使用することもできますが、その場合はunsafeコンテキスト内で使用する必要があります。

using System;
using System.Runtime.InteropServices;

struct Point {
  int X;
  int Y;
}

class Sample {
  static void Main()
  {
    Console.WriteLine("sizeof(int) = {0}", sizeof(int));
    Console.WriteLine("sizeof(long) = {0}", sizeof(long));

    unsafe {
      Console.WriteLine("sizeof(Point) = {0}", sizeof(Point));
    }
  }
}
実行結果
sizeof(int) = 4
sizeof(long) = 8
sizeof(Point) = 8

§2.1 Marshal.SizeOfとsizeofの違い

型によってはMarshal.SizeOfsizeofで異なる結果となる場合があります。 例えばbool型では次のように異なる結果を返します。

Marshal.SizeOfとsizeof
using System;
using System.Runtime.InteropServices;

class Sample {
  static void Main()
  {
    Console.WriteLine("Marshal.SizeOf(typeof(bool)) = {0}", Marshal.SizeOf(typeof(bool)));
    Console.WriteLine("sizeof(bool) = {0}", sizeof(bool));
  }
}
実行結果
Marshal.SizeOf(typeof(bool)) = 4
sizeof(bool) = 1

これは、Marshal.SizeOfアンマネージAPI呼び出し時にマーシャリングされる際のサイズを返すのに対し、sizeofマネージドコード上でのサイズを返すという違いがあるためです。

マネージドコードではbool型は1バイトとされているためsizeof(bool)1を返します。 一方、アンマネージAPI呼び出しに際してbool型はWin32 BOOL型にマーシャリングされます。 このBOOL型はtypedef int BOOL;(=4バイト符号付き整数)とされているため、Marshal.SizeOf(typeof(bool))4を返します。

§3 アラインメント(Pack)・サイズ(Size)

Marshal.SizeOfおよびsizeofは構造体のアラインメントに応じた結果を返します。 次の例ではStructLayout属性Packパラメータを指定して構造体のアラインメントを変更しています。

Marshal.SizeOfとsizeof
using System;
using System.Runtime.InteropServices;

// デフォルトのアラインメント (4バイト)
struct S1 {
  byte F1;
  int  F2;
}

// 1バイトアラインメント
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct S2 {
  byte F1; // 1バイト目に配置される
  int  F2; // 2〜5バイト目に配置される
}

// 2バイトアラインメント
[StructLayout(LayoutKind.Sequential, Pack = 2)]
struct S3 {
  byte F1; // 1バイト目に配置される
  int  F2; // 3〜6バイト目に配置される
}

// 4バイトアラインメント
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct S4 {
  byte F1; // 1バイト目に配置される
  int  F2; // 5〜8バイト目に配置される
}

class Sample {
  static void Main()
  {
    Console.WriteLine("Marshal.SizeOf(typeof(S1)) = {0}", Marshal.SizeOf(typeof(S1)));
    Console.WriteLine("Marshal.SizeOf(typeof(S2)) = {0}", Marshal.SizeOf(typeof(S2)));
    Console.WriteLine("Marshal.SizeOf(typeof(S3)) = {0}", Marshal.SizeOf(typeof(S3)));
    Console.WriteLine("Marshal.SizeOf(typeof(S4)) = {0}", Marshal.SizeOf(typeof(S4)));
    Console.WriteLine();

    unsafe {
      Console.WriteLine("sizeof(typeof(S1)) = {0}", sizeof(S1));
      Console.WriteLine("sizeof(typeof(S2)) = {0}", sizeof(S2));
      Console.WriteLine("sizeof(typeof(S3)) = {0}", sizeof(S3));
      Console.WriteLine("sizeof(typeof(S4)) = {0}", sizeof(S4));
    }
  }
}
実行結果
Marshal.SizeOf(typeof(S1)) = 8
Marshal.SizeOf(typeof(S2)) = 5
Marshal.SizeOf(typeof(S3)) = 6
Marshal.SizeOf(typeof(S4)) = 8

sizeof(typeof(S1)) = 8
sizeof(typeof(S2)) = 5
sizeof(typeof(S3)) = 6
sizeof(typeof(S4)) = 8

同様にSizeパラメータを指定して構造体のサイズを指定した場合もそれに応じた結果となります。

using System;
using System.Runtime.InteropServices;

// 1バイトアラインメント
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct S1 {
  byte F1;
  int  F2;
}

// 1バイトアラインメントで8バイト分確保する
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 8)]
struct S2 {
  byte F1;
  int  F2;
}

// 1バイトアラインメントで16バイト分確保する
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 16)]
struct S3 {
  byte F1;
  int  F2;
}

class Sample {
  static void Main()
  {
    Console.WriteLine("Marshal.SizeOf(typeof(S1)) = {0}", Marshal.SizeOf(typeof(S1)));
    Console.WriteLine("Marshal.SizeOf(typeof(S2)) = {0}", Marshal.SizeOf(typeof(S2)));
    Console.WriteLine("Marshal.SizeOf(typeof(S3)) = {0}", Marshal.SizeOf(typeof(S3)));
    Console.WriteLine();

    unsafe {
      Console.WriteLine("sizeof(typeof(S1)) = {0}", sizeof(S1));
      Console.WriteLine("sizeof(typeof(S2)) = {0}", sizeof(S2));
      Console.WriteLine("sizeof(typeof(S3)) = {0}", sizeof(S3));
    }
  }
}
実行結果
Marshal.SizeOf(typeof(S1)) = 5
Marshal.SizeOf(typeof(S2)) = 8
Marshal.SizeOf(typeof(S3)) = 16

sizeof(typeof(S1)) = 5
sizeof(typeof(S2)) = 8
sizeof(typeof(S3)) = 16