ここでは構造体のサイズの取得を取得する方法としてMarshal.SizeOfメソッドsizeof演算子(C#)、Len関数(VB)について解説します。 これらは多くの場合同じ結果を返しますが、いくつかの相違点があります。 (§.Marshal.SizeOfとsizeofの違い)

また、StructLayoutAttribute属性によってアラインメント(Pack)とサイズ(Size)を指定した場合の動作についても解説します。

Marshal.SizeOfメソッド

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

Marshal.SizeOfメソッドで型のサイズを取得する
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)));
  }
}
Marshal.SizeOfメソッドで型のサイズを取得する
Imports System
Imports System.Runtime.InteropServices

Class Sample
  Shared Sub Main()
    Console.WriteLine("SizeOf({0}) = {1}", 16, Marshal.SizeOf(16)) ' Integer
    Console.WriteLine("SizeOf({0}) = {1}", 3L, Marshal.SizeOf(3L)) ' Long
    Console.WriteLine("SizeOf({0}) = {1}", 1.414F, Marshal.SizeOf(1.414F)) ' Single
    Console.WriteLine("SizeOf({0}) = {1}", 3.14, Marshal.SizeOf(3.14)) ' Double
    Console.WriteLine()

    Console.WriteLine("SizeOf({0}) = {1}", GetType(Integer), Marshal.SizeOf(GetType(Integer)))
    Console.WriteLine("SizeOf({0}) = {1}", GetType(Long), Marshal.SizeOf(GetType(Long)))
    Console.WriteLine("SizeOf({0}) = {1}", GetType(Single), Marshal.SizeOf(GetType(Single)))
    Console.WriteLine("SizeOf({0}) = {1}", GetType(Double), Marshal.SizeOf(GetType(Double)))
    Console.WriteLine("SizeOf({0}) = {1}", GetType(IntPtr), Marshal.SizeOf(GetType(IntPtr)))
  End Sub
End Class
実行結果
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をスローします。

Marshal.SizeOfメソッドではDateTime型のサイズを取得できない
using System;
using System.Runtime.InteropServices;

class Sample {
  static void Main()
  {
    // DateTime型のサイズは取得できない
    Console.WriteLine("SizeOf({0}) = {1}", typeof(DateTime), Marshal.SizeOf(typeof(DateTime)));
  }
}
Marshal.SizeOfメソッドではDateTime型のサイズを取得できない
Imports System
Imports System.Runtime.InteropServices

Class Sample
  Shared Sub Main()
    ' DateTime型のサイズは取得できない
    Console.WriteLine("SizeOf({0}) = {1}", GetType(DateTime), Marshal.SizeOf(GetType(DateTime)))
  End Sub
End Class
実行結果
ハンドルされていない例外: 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メソッドで構造体のサイズを取得することができます。

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)));
  }
}
Marshal.SizeOfメソッドでユーザー定義の構造体のサイズを取得する
Imports System
Imports System.Runtime.InteropServices

<StructLayout(LayoutKind.Sequential)> _
Structure Point
  Dim X As Integer
  Dim Y As Integer
End Structure

<StructLayout(LayoutKind.Explicit)> _
Structure ARGB
  <FieldOffset(0)> Dim Value As UInteger

  <FieldOffset(0)> Dim B As Byte
  <FieldOffset(1)> Dim G As Byte
  <FieldOffset(2)> Dim R As Byte
  <FieldOffset(3)> Dim A As Byte
End Structure


Class Sample
  Shared Sub Main()
    Console.WriteLine("SizeOf({0}) = {1}", GetType(Point), Marshal.SizeOf(GetType(Point)))
    Console.WriteLine("SizeOf({0}) = {1}", GetType(ARGB), Marshal.SizeOf(GetType(ARGB)))
  End Sub
End Class
実行結果
SizeOf(Point) = 8
SizeOf(ARGB) = 4

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


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

Marshal.SizeOf<T>ジェネリックメソッドで型のサイズを取得する .NET Framework 4.5.1
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
Marshal.SizeOf<T>ジェネリックメソッドで型のサイズを取得する .NET Framework 4.5.1
Imports System
Imports System.Runtime.InteropServices

<StructLayout(LayoutKind.Sequential)> _
Structure Point
  Dim X As Integer
  Dim Y As Integer
End Structure

Class Sample
  Shared Sub Main()
    Console.WriteLine("SizeOf(Of Integer) = {0}", Marshal.SizeOf(Of Integer)())
    Console.WriteLine("SizeOf(Of Point) = {0}", Marshal.SizeOf(Of Point)())
  End Sub
End Class
実行結果
SizeOf(Of Integer) = 4
SizeOf(Of Point) = 8

sizeof演算子 (C#)

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

sizeof演算子で型のサイズを取得する
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

Marshal.SizeOfとsizeofの違い

型によってはMarshal.SizeOfメソッドとsizeof演算子で異なる結果となる場合があります。 例えば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を返します。

Len関数 (VB)

Visual BasicではLen関数によっても型のサイズを取得することができます。 Len関数はMarshal.SizeOfメソッドsizeof演算子とは異なり、型ではなく具体的な値または変数(オブジェクト)を指定することにより、そのサイズを返します。

Len関数が返すサイズは、FilePut関数(VB6以前のPutステートメントに相当)で書き込まれる際のサイズとされています。 具体的には、Boolean2DateTime8とされているほか、基本型(Charを含む数値型)であればそのサイズが返されます。 (型の種類・サイズ・精度・値域 §.型のサイズ・精度と値域) ただし、DateTimeのサイズは8とされている一方で、DateTimeOffset0となるようです。

文字列あるいはChar配列の場合はその長さ・Lengthプロパティの値が返されます。 ただし、VBFixedString属性によって固定長の文字列として定義されているフィールドの場合は、その長さが返されます。 Nothingの場合は例外とはならず0が返されます。 構造体の場合、基本的には構成される基本型の組み合わせに基づいて計算されますが、具体的な動作についてはFilePut関数を参照してください。

Len関数でオブジェクトのサイズを取得する
Imports System

Structure Point
  Public X As Integer
  Public Y As Integer
End Structure

Class Sample
  Shared Sub Main()
    Dim i As Integer = 0
    Dim l As Long = 0L
    Dim d As Decimal = 0.0D
    Dim b As Boolean
    Dim dt As DateTime
    Dim dto As DateTimeOffset
    Dim p As Point

    Console.WriteLine("Len(i) = {0}", Len(i))
    Console.WriteLine("Len(l) = {0}", Len(l))
    Console.WriteLine("Len(b) = {0}", Len(b))
    Console.WriteLine("Len(dt) = {0}", Len(dt))
    Console.WriteLine("Len(dto) = {0}", Len(dto))
    Console.WriteLine("Len(p) = {0}", Len(p))
  End Sub
End Class
実行結果
Len(i) = 4
Len(l) = 8
Len(b) = 2
Len(dt) = 8
Len(dto) = 0
Len(p) = 8

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

Marshal.SizeOfメソッドおよびsizeof演算子は構造体のアラインメントに応じた結果を返します。 一方、Len関数ではアラインメントは無視され、フィールドのサイズのみに基づいた結果を返します。 次の例では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(S1) = {0}", sizeof(S1));
      Console.WriteLine("sizeof(S2) = {0}", sizeof(S2));
      Console.WriteLine("sizeof(S3) = {0}", sizeof(S3));
      Console.WriteLine("sizeof(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(S1) = 8
sizeof(S2) = 5
sizeof(S3) = 6
sizeof(S4) = 8
アラインメントを指定した構造体でのMarshal.SizeOfとLenの結果
Imports System
Imports System.Runtime.InteropServices

' デフォルトのアラインメント (4バイト)
Structure S1
  Dim F1 As Byte
  Dim F2 As Integer
End Structure

' 1バイトアラインメント
<StructLayout(LayoutKind.Sequential, Pack := 1)> _
Structure S2
  Dim F1 As Byte    ' 1バイト目に配置される
  Dim F2 As Integer ' 2〜5バイト目に配置される
End Structure

' 2バイトアラインメント
<StructLayout(LayoutKind.Sequential, Pack := 2)> _
Structure S3
  Dim F1 As Byte    ' 1バイト目に配置される
  Dim F2 As Integer ' 3〜6バイト目に配置される
End Structure

' 4バイトアラインメント
<StructLayout(LayoutKind.Sequential, Pack := 4)> _
Structure S4
  Dim F1 As Byte    ' 1バイト目に配置される
  Dim F2 As Integer ' 5〜8バイト目に配置される
End Structure

Class Sample
  Shared Sub Main()
    Console.WriteLine("Marshal.SizeOf(GetType(S1)) = {0}", Marshal.SizeOf(GetType(S1)))
    Console.WriteLine("Marshal.SizeOf(GetType(S2)) = {0}", Marshal.SizeOf(GetType(S2)))
    Console.WriteLine("Marshal.SizeOf(GetType(S3)) = {0}", Marshal.SizeOf(GetType(S3)))
    Console.WriteLine("Marshal.SizeOf(GetType(S4)) = {0}", Marshal.SizeOf(GetType(S4)))
    Console.WriteLine()

    Dim s1 As S1
    Dim s2 As S2
    Dim s3 As S3
    Dim s4 As S4

    Console.WriteLine("Len(s1) = {0}", Len(s1))
    Console.WriteLine("Len(s2) = {0}", Len(s2))
    Console.WriteLine("Len(s3) = {0}", Len(s3))
    Console.WriteLine("Len(s4) = {0}", Len(s4))
  End Sub
End Class
実行結果
Marshal.SizeOf(GetType(S1)) = 8
Marshal.SizeOf(GetType(S2)) = 5
Marshal.SizeOf(GetType(S3)) = 6
Marshal.SizeOf(GetType(S4)) = 8

Len(s1) = 5
Len(s2) = 5
Len(s3) = 5
Len(s4) = 5

Sizeパラメータを指定して構造体のサイズを指定した場合も同様で、Marshal.SizeOfメソッドおよびsizeof演算子は構造体のサイズに応じた結果を返します。 一方、Len関数ではサイズは無視され、フィールドのサイズのみに基づいた結果を返します。

サイズを指定した構造体でのMarshal.SizeOfとsizeofの結果
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(S1) = {0}", sizeof(S1));
      Console.WriteLine("sizeof(S2) = {0}", sizeof(S2));
      Console.WriteLine("sizeof(S3) = {0}", sizeof(S3));
    }
  }
}
実行結果
Marshal.SizeOf(typeof(S1)) = 5
Marshal.SizeOf(typeof(S2)) = 8
Marshal.SizeOf(typeof(S3)) = 16

sizeof(S1) = 5
sizeof(S2) = 8
sizeof(S3) = 16
サイズを指定した構造体でのMarshal.SizeOfとLenの結果
Imports System
Imports System.Runtime.InteropServices

' 1バイトアラインメント
<StructLayout(LayoutKind.Sequential, Pack := 1)> _
Structure S1
  Dim F1 As Byte
  Dim F2 As Integer
End Structure

' 1バイトアラインメントで8バイト分確保する
<StructLayout(LayoutKind.Sequential, Pack := 1, Size := 8)> _
Structure S2
  Dim F1 As Byte
  Dim F2 As Integer
End Structure

' 1バイトアラインメントで16バイト分確保する
<StructLayout(LayoutKind.Sequential, Pack := 1, Size := 16)> _
Structure S3
  Dim F1 As Byte
  Dim F2 As Integer
End Structure

Class Sample
  Shared Sub Main()
    Console.WriteLine("Marshal.SizeOf(GetType(S1)) = {0}", Marshal.SizeOf(GetType(S1)))
    Console.WriteLine("Marshal.SizeOf(GetType(S2)) = {0}", Marshal.SizeOf(GetType(S2)))
    Console.WriteLine("Marshal.SizeOf(GetType(S3)) = {0}", Marshal.SizeOf(GetType(S3)))
    Console.WriteLine()

    Dim s1 As S1
    Dim s2 As S2
    Dim s3 As S3

    Console.WriteLine("Len(s1) = {0}", Len(s1))
    Console.WriteLine("Len(s2) = {0}", Len(s2))
    Console.WriteLine("Len(s3) = {0}", Len(s3))
  End Sub
End Class
実行結果
Marshal.SizeOf(GetType(S1)) = 5
Marshal.SizeOf(GetType(S2)) = 8
Marshal.SizeOf(GetType(S3)) = 16

Len(s1) = 5
Len(s2) = 5
Len(s3) = 5