ここでは構造体のサイズの取得を取得する方法としてMarshal.SizeOfメソッド、sizeof演算子(C#)、Len関数(VB)について解説します。 これらは多くの場合同じ結果を返しますが、いくつかの相違点があります。 (§.Marshal.SizeOfとsizeofの違い)
また、StructLayoutAttribute属性によってアラインメント(Pack)とサイズ(Size)を指定した場合の動作についても解説します。
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)));
}
}
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をスローします。
using System;
using System.Runtime.InteropServices;
class Sample {
static void Main()
{
// DateTime型のサイズは取得できない
Console.WriteLine("SizeOf({0}) = {1}", typeof(DateTime), Marshal.SizeOf(typeof(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
のほか、DateTimeOffset
、string
やobject
などの参照型のもサイズが取得できません。
構造体の場合では、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)));
}
}
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
を指定するだけで型のサイズが取得できるようになります。
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
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
演算子はint
やbool
などの組み込み型に対して使用することができ、定義済みのサイズを返します。 また、参照型のメンバを含まない構造体に対して使用することもできますが、その場合は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
Marshal.SizeOfとsizeofの違い
型によってはMarshal.SizeOf
メソッドとsizeof
演算子で異なる結果となる場合があります。 例えばbool型では次のように異なる結果を返します。
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ステートメントに相当)で書き込まれる際のサイズとされています。 具体的には、Boolean
が2、DateTime
が8とされているほか、基本型(Char
を含む数値型)であればそのサイズが返されます。 (型の種類・サイズ・精度・値域 §.型のサイズ・精度と値域) ただし、DateTime
のサイズは8とされている一方で、DateTimeOffset
は0となるようです。
文字列あるいはChar配列の場合はその長さ・Lengthプロパティの値が返されます。 ただし、VBFixedString属性によって固定長の文字列として定義されているフィールドの場合は、その長さが返されます。 Nothing
の場合は例外とはならず0が返されます。 構造体の場合、基本的には構成される基本型の組み合わせに基づいて計算されますが、具体的な動作についてはFilePut関数を参照してください。
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
パラメータを指定して構造体のアラインメントを変更しています。
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
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
関数ではサイズは無視され、フィールドのサイズのみに基づいた結果を返します。
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
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