2014-10-29T23:56:21の更新内容

programming/netfx/struct/1_structlayout_fieldoffset/index.wiki.txt

current previous
1,11 1,11
~
${smdncms:title,フィールドのレイアウト・オフセット}
${smdncms:title,構造体のレイアウトと共用体}
~
${smdncms:header_title,フィールドのレイアウト・オフセット (StructLayoutAttribute, FieldOffsetAttribute)}
${smdncms:header_title,構造体のレイアウトと共用体 (StructLayoutAttribute, FieldOffsetAttribute)}
~
${smdncms:keywords,構造体,共用体,フィールド,レイアウト,オフセット,サイズ,アラインメント,パッキングサイズ,union,StructLayout,FieldOffset,Marshal.OffsetOf}
${smdncms:keywords,構造体,共用体,レイアウト,StructLayout,FieldOffset,union}
 
${smdncms:document_versions,codelang=cs,codelang=vb}
${smdncms:document_versions,codelang=cs,codelang=vb}
 

        

        
 
#navi(..)
#navi(..)
 

        

        
~
.NET Frameworkでは[[属性>programming/netfx/attributes]]によって構造体(またはクラス)のフィールドのレイアウトを指定することができます。 例えば[[FieldOffset属性>#FieldOffsetAttribute]]を使用すれば各フィールドのオフセットを明示的に指定することができます。 また、[[StructLayout属性>#StructLayoutAttribute]]を使用すれば構造体の[[アラインメント(パッキングサイズ)を指定>#StructLayoutAttribute.Pack]]することができます。 アンマネージAPI呼び出しや構造を持ったバイナリデータを扱う場合には[[StructLayout属性>#StructLayoutAttribute]]と[[FieldOffset属性>#FieldOffsetAttribute]]が非常に役立ちます。
.NET Framework の共通言語仕様(CLS)には共用体(C/C++のunion)は存在しません。 また、C#にもVB.NETにも共用体は存在しません。 共用体自体はそれほど使う機会が多いというわけではないですし、また、なければならないというものでもありません。 しかし、ごく稀にあると便利かなと思うことがあります。 そういうときに、.NET Frameworkの&msdn(netfx,type,System.Runtime.InteropServices.StructLayoutAttribute){StructLayout};属性と&msdn(netfx,type,System.Runtime.InteropServices.FieldOffsetAttribute){FieldOffset};属性を用いることでC#やVB.NETの構造体でも共用体の機能を再現することができます。
 

        

        
 
-関連するページ
-関連するページ
 
--[[programming/netfx/tips/convert_struct_and_bytearray]]
--[[programming/netfx/tips/convert_struct_and_bytearray]]
17,165 17,50
 

        

        
 
#adunit
#adunit
 

        

        
~
*フィールドのレイアウト (StructLayout属性) [#StructLayoutAttribute]
*StructLayout属性とFieldOffset属性
~
&msdn(netfx,type,System.Runtime.InteropServices.StructLayoutAttribute){StructLayout};属性は、メモリ上でのフィールド(メンバ変数)の配置方法を指定するための属性です。 StructLayout属性は構造体だけでなくクラスにも適用することができます。 フィールドの配置方法は&msdn(netfx,type,System.Runtime.InteropServices.LayoutKind){LayoutKind};列挙体で指定することができ、次のいずれかを指定することが出来ます。
それではまず&msdn(netfx,type,System.Runtime.InteropServices.StructLayoutAttribute){StructLayout};属性について見てみることにします。 この属性は構造体(クラスでも可)に対して適用し、この属性を適用することで構造体の各メンバ変数(フィールド)のメモリ上での配置方法を指定することができます。 配置方法は、&msdn(netfx,type,System.Runtime.InteropServices.LayoutKind){LayoutKind};で指定することができ、次のいずれかを指定することが出来ます。
-
:LayoutKind.Auto|ランタイムが自動的に、最適な方法でメンバを配置 (StructLayout属性を指定しない場合と同じ)
-
:LayoutKind.Sequential|ランタイムによる自動的な並べ替えを行わず、記述した順序のままメンバを配置
-
:LayoutKind.Explicit|プログラム側で明示的に位置を指定してメンバを配置
 

        

        
~
:LayoutKind.Auto|ランタイムが自動的に最適な順序でフィールドを配置する (StructLayout属性を指定しない場合と同じ)
LayoutKind.Explicitを指定した場合は、全てのメンバ変数に対して明示的に位置を指定しなければなりません。 メンバ変数の位置を指定するために使用する属性が&msdn(netfx,type,System.Runtime.InteropServices.FieldOffsetAttribute){FieldOffset};属性です。 この属性では構造体の先頭からのオフセット値をバイト単位で指定します。
+
:LayoutKind.Sequential|ランタイムによる自動的な並べ替えを行わず、コード上で記述されている順序のままフィールドを配置する
+
:LayoutKind.Explicit|明示的に位置(オフセット)を指定してフィールドを配置する (オフセットは後述の[[FieldOffset属性>#FieldOffsetAttribute]]で指定する)
 

        

        
~
構造体にStructLayout属性を適用する場合の例は次のようになります。
それでは、早速これらの属性を適用した構造体を作成してみようと思います。 このサンプルではshortではなく System.UInt16を用いていますが、16Bitの符号無し整数型を用いていると言うことを明かにするためであって、それ以上の意味はありません。
~

          
#tabpage(codelang=cs)
+
#tabpage(codelang=cs,container-title=構造体にStructLayout属性を適用する例)
 
#code{{
#code{{
 
using System;
using System;
 
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
 

        

        
~
// フィールドのレイアウトを特に指定しない構造体
// StructLayout属性及びFieldOffset属性を適用した構造体
+
// (構造体内のフィールドはランタイムによって最適な位置に配置される
+
// そのため、フィールドの型や数によっては記述されている順序と実際の配置が異なる場合もありうる)
+
struct S1 {
+
  int F1;
+
  int F2;
+
}
+

          
+
// フィールドのレイアウトを特に指定しない構造体 (上記の構造体S1と同等)
+
[StructLayout(LayoutKind.Auto)]
+
struct S2 {
+
  int F1;
+
  int F2;
+
}
+

          
+
// StructLayoutにLayoutKind.Sequentialを指定した構造体
+
// (構造体内のフィールドは記述されたままの順序、つまりこの構造体S3ではF1→F2の順に配置される)
+
[StructLayout(LayoutKind.Sequential)]
+
struct S3 {
+
  int F1;
+
  int F2;
+
}
+

          
+
// StructLayoutにLayoutKind.Explicitを指定した構造体
+
// (構造体内のフィールドはFieldOffsetで指定されたオフセットに配置される
+
// この例ではF1は構造体の先頭から0バイト、F2は先頭から4バイトの位置に配置される)
 
[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Explicit)]
~
struct S4 {
struct SampleStruct
~
  [FieldOffset(0)] int F1;
{
~
  [FieldOffset(4)] int F2;
  [FieldOffset(0)] public System.UInt16 Value1;
-
  [FieldOffset(2)] public System.UInt16 Value2;
-
  [FieldOffset(4)] public System.UInt16 Value3;
-
  [FieldOffset(6)] public System.UInt16 Value4;
 
}
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Runtime.InteropServices
 

        

        
~
' フィールドのレイアウトを特に指定しない構造体
class SampleClass
~
' (構造体内のフィールドはランタイムによって最適な位置に配置される
{
+
' そのため、フィールドの型や数によっては記述されている順序と実際の配置が異なる場合もありうる)
+
Structure S1
+
  Dim F1 As Integer
+
  Dim F2 As Integer
+
End Structure
+

          
+
' フィールドのレイアウトを特に指定しない構造体 (上記の構造体S1と同等)
+
<StructLayout(LayoutKind.Auto)> _
+
Structure S2
+
  Dim F1 As Integer
+
  Dim F2 As Integer
+
End Structure
+

          
+
' StructLayoutにLayoutKind.Sequentialを指定した構造体
+
' (構造体内のフィールドは記述されたままの順序、つまりこの構造体S3ではF1→F2の順に配置される)
+
<StructLayout(LayoutKind.Sequential)> _
+
Structure S3
+
  Dim F1 As Integer
+
  Dim F2 As Integer
+
End Structure
+

          
+
' StructLayoutにLayoutKind.Explicitを指定した構造体
+
' (構造体内のフィールドはFieldOffsetで指定されたオフセットに配置される
+
' この例ではF1は構造体の先頭から0バイト、F2は先頭から4バイトの位置に配置される)
+
<StructLayout(LayoutKind.Explicit)> _
+
Structure S4
+
  <FieldOffset(0)> Dim F1 As Integer
+
  <FieldOffset(4)> Dim F2 As Integer
+
End Structure
+
}}
+
#tabpage-end
+

          
+
``LayoutKind.Explicit``を指定した場合、''すべてのフィールドに対して''FieldOffset属性を指定する必要があります。 また、構造体(またはクラス)をアンマネージAPI呼び出しで使用する場合、``LayoutKind.Sequential``または``LayoutKind.Explicit``が指定されている必要があります。
+

          
+
``LayoutKind``のほか、StructLayout属性では構造体の[[アラインメント>#StructLayoutAttribute.Pack]]と[[サイズ>#StructLayoutAttribute.Size]]を指定することもできます。
+

          
+
*フィールドのオフセット (FieldOffset属性) [#FieldOffsetAttribute]
+
&msdn(netfx,type,System.Runtime.InteropServices.FieldOffsetAttribute){FieldOffset};属性は構造体(またはクラス)内における各フィールドの位置(オフセット)を指定するための属性です。 構造体に``LayoutKind.Explicit``を指定した場合には''すべてのフィールドに対して''FieldOffset属性を指定し、オフセットを明示的に指定する必要があります。 この属性では、構造体の先頭からのオフセット値をバイト単位で指定します。
+

          
+
#tabpage(codelang=cs,container-title=フィールドにFieldOffset属性を適用する例)
+
#code{{
+
using System;
+
using System.Runtime.InteropServices;
+

          
+
// フィールドにFieldOffsetを指定した構造体
+
// (FieldOffsetを指定する場合はLayoutKind.Explicitを指定する必要がある)
+
[StructLayout(LayoutKind.Explicit)]
+
struct S1 {
+
  [FieldOffset(0)] int F1; // このフィールドは構造体の先頭から0バイトの位置に配置される
+
  [FieldOffset(4)] int F2; // このフィールドは構造体の先頭から4バイトの位置に配置される
+
}
+

          
+
[StructLayout(LayoutKind.Explicit)]
+
struct S2 {
+
  // フィールド同士を不連続に配置する(フィールドから参照されない領域を作る)こともできる
+
  [FieldOffset(4)]  int F1;
+
  [FieldOffset(12)] int F2;
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Runtime.InteropServices
+

          
+
' フィールドにFieldOffsetを指定した構造体
+
' (FieldOffsetを指定する場合はLayoutKind.Explicitを指定する必要がある)
+
<StructLayout(LayoutKind.Explicit)> _
+
Structure S1
+
  <FieldOffset(0)> Dim F1 As Integer ' このフィールドは構造体の先頭から0バイトの位置に配置される
+
  <FieldOffset(4)> Dim F2 As Integer ' このフィールドは構造体の先頭から4バイトの位置に配置される
+
End Structure
+

          
+
<StructLayout(LayoutKind.Explicit)> _
+
Structure S2
+
  ' フィールド同士を不連続に配置する(フィールドから参照されない領域を作る)こともできる
+
  <FieldOffset(4)> Dim F1 As Integer
+
  <FieldOffset(12)> Dim F2 As Integer
+
End Structure
+
}}
+
#tabpage-end
+

          
+
FieldOffset属性を指定している構造体の場合でも、使用方法や動作は通常の構造体と変わりません。
+

          
+
#tabpage(codelang=cs,container-title=FieldOffset属性を適用した構造体を使用する例)
+
#code{{
+
using System;
+
using System.Runtime.InteropServices;
+

          
+
[StructLayout(LayoutKind.Explicit)]
+
struct S1 {
+
  [FieldOffset(0)] public int F1;
+
  [FieldOffset(4)] public int F2;
+
}
+

          
+
class Sample {
 
  static void Main()
  static void Main()
 
  {
  {
~
    S1 s = new S1();
    SampleStruct s = new SampleStruct();
 

        

        
~
    // 各フィールドに値を設定
    // 各フィールドに値を指定
~
    s.F1 = 0x00112233;
    s.Value1 = 0x1122;
~
    s.F2 = 0x44556677;
    s.Value2 = 0x3344;
-
    s.Value3 = 0x5566;
-
    s.Value4 = 0x7788;
 

        

        
 
    // 各フィールドの値を表示
    // 各フィールドの値を表示
~
    Console.WriteLine("s.F1 = 0x{0:X8}", s.F1);
    Console.WriteLine("SampleStruct.Value1 : 0x" + s.Value1.ToString("X4"));
~
    Console.WriteLine("s.F2 = 0x{0:X8}", s.F2);
    Console.WriteLine("SampleStruct.Value2 : 0x" + s.Value2.ToString("X4"));
-
    Console.WriteLine("SampleStruct.Value3 : 0x" + s.Value3.ToString("X4"));
-
    Console.WriteLine("SampleStruct.Value4 : 0x" + s.Value4.ToString("X4"));
-

          
-
    // 構造体のサイズを表示
-
    Console.WriteLine("SizeOf(SampleStruct) = {0}", Marshal.SizeOf(typeof(SampleStruct)));
 
  }
  }
 
}
}
 
}}
}}
184,189 69,196
 
Imports System
Imports System
 
Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices
 

        

        
-
' StructLayout属性及びFieldOffset属性を適用した構造体
 
<StructLayout(LayoutKind.Explicit)> _
<StructLayout(LayoutKind.Explicit)> _
~
Structure S1
Structure SampleStruct
~
  <FieldOffset(0)> Public F1 As Integer
  <FieldOffset(0)> Public Value1 As System.UInt16
~
  <FieldOffset(4)> Public F2 As Integer
  <FieldOffset(2)> Public Value2 As System.UInt16
~
End Structure
  <FieldOffset(4)> Public Value3 As System.UInt16
-
  <FieldOffset(6)> Public Value4 As System.UInt16
-
End Structure
-

          
-
Class SampleClass
-
  Shared Sub Main()
-
    Dim s As New SampleStruct()
-

          
-
    ' 各フィールドに値を指定
-
    s.Value1 = &h1122
-
    s.Value2 = &h3344
-
    s.Value3 = &h5566
-
    s.Value4 = &h7788
 

        

        
~
Class Sample
    ' 各フィールドの値を表示
~
  Public Shared Sub Main()
    Console.WriteLine("SampleStruct.Value1 : 0x" + s.Value1.ToString("X4"))
~
    Dim s As New S1()
    Console.WriteLine("SampleStruct.Value2 : 0x" + s.Value2.ToString("X4"))
~

          
    Console.WriteLine("SampleStruct.Value3 : 0x" + s.Value3.ToString("X4"))
~
    ' 各フィールドに値を設定
    Console.WriteLine("SampleStruct.Value4 : 0x" + s.Value4.ToString("X4"))
+
    s.F1 = &H00112233
+
    s.F2 = &H44556677
 

        

        
~
    ' 各フィールドの値を表示
    ' 構造体のサイズを表示
~
    Console.WriteLine("s.F1 = 0x{0:X8}", s.F1)
    Console.WriteLine("SizeOf(SampleStruct) = {0}", Marshal.SizeOf(GetType(SampleStruct)))
+
    Console.WriteLine("s.F2 = 0x{0:X8}", s.F2)
 
  End Sub
  End Sub
 
End Class
End Class
 
}}
}}
 
#tabpage-end
#tabpage-end
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
s.F1 = 0x00112233
SampleStruct.Value1 : 0x1122
~
s.F2 = 0x44556677
SampleStruct.Value2 : 0x3344
-
SampleStruct.Value3 : 0x5566
-
SampleStruct.Value4 : 0x7788
-
SizeOf(SampleStruct) = 8
-
Press any key to continue
 
}}
}}
 

        

        
~
この結果にもあるとおり通常の構造体の場合と何ら変わりありませんが、しかしメモリ上の配置は次の図のようになっているはずです。 (図はリトルエンディアン環境でのものです)
この結果を見てわかるとおり、通常の構造体の場合と何ら変わりないように思われます。 しかし、メモリ上の配置は次の図のようになっているはずです。
 

        

        
 
#html{{
#html{{
 
<table>
<table>
~
  <caption>メモリ上での変数sと各フィールドの配置</caption>
  <caption>SampleStructの各フィールドとメモリ上の配置</caption>
 
  <thead>
  <thead>
 
    <tr>
    <tr>
~
      <th>オフセット<br/>(バイト)</th>
      <th>オフセット値<br/>(バイト)</th>
 
      <th>フィールド</th>
      <th>フィールド</th>
~
      <th>値</th>
      <th>格納値</th>
 
    </tr>
    </tr>
 
  </thead>
  </thead>
 
  <tbody>
  <tbody>
 
    <tr>
    <tr>
 
      <td>0</td>
      <td>0</td>
~
      <td rowspan="4">F1</td>
      <td rowspan="2">Value1</td>
~
      <td style="border-bottom-style: dashed;">0x33</td>
      <td style="border-bottom-style: dashed;">0x22</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>1</td>
      <td>1</td>
~
      <td style="border-top-style: dashed; border-bottom-style: dashed;">0x22</td>
      <td style="border-top-style: dashed;">0x11</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>2</td>
      <td>2</td>
~
      <td style="border-top-style: dashed; border-bottom-style: dashed;">0x11</td>
      <td rowspan="2">Value2</td>
-
      <td style="border-bottom-style: dashed;">0x44</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>3</td>
      <td>3</td>
~
      <td style="border-top-style: dashed;">0x00</td>
      <td style="border-top-style: dashed;">0x33</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>4</td>
      <td>4</td>
~
      <td rowspan="4">F2</td>
      <td rowspan="2">Value3</td>
~
      <td style="border-bottom-style: dashed;">0x77</td>
      <td style="border-bottom-style: dashed;">0x66</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>5</td>
      <td>5</td>
~
      <td style="border-top-style: dashed; border-bottom-style: dashed;">0x66</td>
      <td style="border-top-style: dashed;">0x55</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>6</td>
      <td>6</td>
~
      <td style="border-top-style: dashed; border-bottom-style: dashed;">0x55</td>
      <td rowspan="2">Value4</td>
-
      <td style="border-bottom-style: dashed;">0x88</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>7</td>
      <td>7</td>
~
      <td style="border-top-style: dashed;">0x44</td>
      <td style="border-top-style: dashed;">0x77</td>
 
    </tr>
    </tr>
 
  </tbody>
  </tbody>
 
</table>
</table>
 
}}
}}
 

        

        
-
LayoutKind.ExplicitではなくLayoutKind.Sequentialを指定した場合、&msdn(netfx,member,System.Runtime.InteropServices.StructLayoutAttribute.Pack){StructLayoutAttribute.Packフィールド};で構造体のアライメントを指定することも出来ますが、詳細はここでは省略します。
 

        

        
~
**共用体の実装 [#ImplementUnion]
*共用体を作る [#union]
~
FieldOffset属性では、他のフィールドと同じオフセットを指定することもできます。 つまり、複数のフィールドが同一のメモリ領域を参照するようにオフセットを指定することができます。 これにより、FieldOffset属性を使って''共用体''(``union``)と同じ構造を作ることができます。 C#やVBでは共用体を直接作成する言語機能はありませんが、構造体とFieldOffset属性を組み合わせることによって共用体となる構造体をつくることができます。
このように、StructLayout属性で明示的にメンバ変数の配置方法を指定することができ、FieldOffsetでメンバ変数のオフセット値を指定することが出来るとなれば、共用体を作る方法はおのずと浮かんでくるはずです。 共用体では一つ以上のメンバ変数が、同じメモリ領域を共用します。 つまり、メンバ変数のオフセット値を全て同じにすれば、共用体と同じ構造の構造体を作ることができることになります。
 

        

        
~
例として上位ワード(2バイト)と下位ワードを参照するフィールドと、ダブルワード(4バイト)を参照するフィールドを持つ共用体を作成すると次のようになります。
#tabpage(codelang=cs)
+

          
+
#tabpage(codelang=cs,container-title=FieldOffset属性を使って共用体を作成する例)
 
#code{{
#code{{
 
using System;
using System;
 
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
 

        

        
-
// 構造体で再現した共用体
 
[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Explicit)]
~
struct DWORD {
struct DoubleWord
~
  // ダブルワード(オフセット0から4バイト分)を格納・参照するフィールド
{
~
  [FieldOffset(0)] public uint Value;
  [FieldOffset(0)] public System.UInt32 Value;
+

          
+
  // 下位ワード(オフセット0から2バイト分)を格納・参照するフィールド
+
  [FieldOffset(0)] public ushort LoWord;
 

        

        
~
  // 上位ワード(オフセット2から2バイト分)を格納・参照するフィールド
  [FieldOffset(0)] public System.UInt16 LowWord;
~
  [FieldOffset(2)] public ushort HiWord;
  [FieldOffset(2)] public System.UInt16 HighWord;
 
}
}
 

        

        
~
class Sample {
class SampleClass
-
{
 
  static void Main()
  static void Main()
 
  {
  {
~
    DWORD dw = new DWORD();
    DoubleWord dw = new DoubleWord();
 

        

        
~
    // ダブルワード値を設定
    // ダブルワード値を指定
 
    dw.Value = 0x11223344;
    dw.Value = 0x11223344;
 

        

        
~
    // 設定したダブルワード値を表示
    // 下位ワードと上位ワードを表示
~
    Console.WriteLine("Value = 0x{0:X8}", dw.Value);
    Console.WriteLine(" Low word: 0x" + dw.LowWord.ToString("X4"));
-
    Console.WriteLine("High word: 0x" + dw.HighWord.ToString("X4"));
 

        

        
~
    // 下位ワードと上位ワードの値を表示
    // 構造体のサイズを表示
~
    Console.WriteLine("LoWord = 0x{0:X4}", dw.LoWord);
    Console.WriteLine("SizeOf(DoubleWord) = {0}", Marshal.SizeOf(typeof(DoubleWord)));
+
    Console.WriteLine("HiWord = 0x{0:X4}", dw.HiWord);
 
  }
  }
 
}
}
 
}}
}}
+

          
+
#prompt(実行結果){{
+
Value = 0x11223344
+
LoWord = 0x3344
+
HiWord = 0x1122
+
}}
 
#tabpage(codelang=vb)
#tabpage(codelang=vb)
 
#code{{
#code{{
 
Imports System
Imports System
 
Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices
 

        

        
-
' 構造体で再現した共用体
 
<StructLayout(LayoutKind.Explicit)> _
<StructLayout(LayoutKind.Explicit)> _
~
Structure DWORD
Structure DoubleWord
~
  ' ダブルワード(オフセット0から4バイト分)を格納・参照するフィールド
  <FieldOffset(0)> Public Value As System.UInt32
~
  <FieldOffset(0)> Public Value As UInteger

          
-
  <FieldOffset(0)> Public LowWord As System.UInt16
-
  <FieldOffset(2)> Public HighWord As System.UInt16
-
End Structure
-

          
-
Class SampleClass
-
  Shared Sub Main()
-
    Dim dw As New DoubleWord()
 

        

        
~
  ' 下位ワード(オフセット0から2バイト分)を格納・参照するフィールド
    ' ダブルワード値を指定
~
  <FieldOffset(0)> Public LoWord As UShort
    dw.Value = &h11223344
 

        

        
~
  ' 上位ワード(オフセット2から2バイト分)を格納・参照するフィールド
    ' 各フィールドの値を表示
~
  <FieldOffset(2)> Public HiWord As UShort
    Console.WriteLine(" Low word: 0x" + dw.LowWord.ToString("X4"))
~
End Structure
    Console.WriteLine("High word: 0x" + dw.HighWord.ToString("X4"))
 

        

        
~
Class Sample
    ' 構造体のサイズを表示
~
  Public Shared Sub Main()
    Console.WriteLine("SizeOf(DoubleWord) = {0}", Marshal.SizeOf(GetType(DoubleWord)))
+
    Dim dw As New DWORD()
+

          
+
    ' ダブルワード値を設定
+
    dw.Value = &H11223344
+

          
+
    ' 設定したダブルワード値を表示
+
    Console.WriteLine("Value = &H{0:X8}", dw.Value)
+

          
+
    ' 下位ワードと上位ワードの値を表示
+
    Console.WriteLine("LoWord = &H{0:X4}", dw.LoWord)
+
    Console.WriteLine("HiWord = &H{0:X4}", dw.HiWord)
 
  End Sub
  End Sub
 
End Class
End Class
 
}}
}}
-
#tabpage-end
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
Value = &H11223344
 Low word: 0x3344
~
LoWord = &H3344
High word: 0x1122
~
HiWord = &H1122
SizeOf(DoubleWord) = 4
-
Press any key to continue
 
}}
}}
+
#tabpage-end
 

        

        
~
実行結果からも共用体と同等の動作となっていることが分かります。 メモリ上の配置は次のようになっているはずです。
この結果から、この構造体が共用体と同様の機能を得ていることがわかります。 メモリ上の配置は次のようになっています。
 

        

        
 
#html{{
#html{{
 
<table>
<table>
~
  <caption>メモリ上での変数dwと各フィールドの配置</caption>
  <caption>DoubleWordの各フィールドとメモリ上の配置</caption>
 
  <thead>
  <thead>
 
    <tr>
    <tr>
~
      <th>オフセット<br/>(バイト)</th>
      <th>オフセット値<br/>(バイト)</th>
~
      <th colspan="2">フィールド</th>
      <th>フィールド</th>
~
      <th>値</th>
      <th>格納値</th>
-
      <th>フィールド</th>
 
    </tr>
    </tr>
 
  </thead>
  </thead>
 
  <tbody>
  <tbody>
 
    <tr>
    <tr>
 
      <td>0</td>
      <td>0</td>
 
      <td rowspan="4">Value</td>
      <td rowspan="4">Value</td>
+
      <td rowspan="2" style="border-bottom-style: dashed;">LoWord</td>
 
      <td style="border-bottom-style: dashed;">0x44</td>
      <td style="border-bottom-style: dashed;">0x44</td>
-
      <td rowspan="2">LowWord</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>1</td>
      <td>1</td>
374,8 266,8
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>2</td>
      <td>2</td>
+
      <td rowspan="2" style="border-top-style: dashed;">HiWord</td>
 
      <td style="border-top-style: dashed; border-bottom-style: dashed;">0x22</td>
      <td style="border-top-style: dashed; border-bottom-style: dashed;">0x22</td>
-
      <td rowspan="2">HighWord</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>3</td>
      <td>3</td>
385,77 277,82
 
</table>
</table>
 
}}
}}
 

        

        
~
参考までに、これと同等の結果を得るためのC++コードを次に示します。
メンバ変数Valueは構造体の0バイト目から4バイト分、LowWordは同じく0バイト目から2バイト分、HighWordは2バイト目から2バイト分の位置に配置されます。 参考に、これと同様の結果を得るためのC++コードを次に示します。
 

        

        
 
#code(c){{
#code(c){{
 
#include <iostream>
#include <iostream>
 

        

        
 
using namespace std;
using namespace std;
 

        

        
~
union DWORD
union DoubleWord
 
{
{
 
  unsigned long Value;
  unsigned long Value;
 

        

        
 
  struct
  struct
 
  {
  {
~
    unsigned short LoWord;
    unsigned short LowWord;
~
    unsigned short HiWord;
    unsigned short HighWord;
 
  } DWord;
  } DWord;
 
};
};
 

        

        
 
int main()
int main()
 
{
{
~
  DWORD dw;
  DoubleWord dw;
 

        

        
 
  dw.Value = 0x11223344;
  dw.Value = 0x11223344;
 

        

        
~
  cout << "LoWord = 0x" << hex << dw.DWord.LoWord << endl;
  cout << " Low word: 0x" << hex << dw.DWord.LowWord << endl;
~
  cout << "HiWord = 0x" << hex << dw.DWord.HiWord << endl;
  cout << "High word: 0x" << hex << dw.DWord.HighWord << endl;
-

          
-
  cout << "sizeof(DoubleWord) = " << sizeof(DoubleWord) << endl;
 

        

        
 
  return 0;
  return 0;
 
}
}
 
}}
}}
 

        

        
-
*共用体の有効な利用法
-
これまでのサンプルで、メンバ変数のオフセット値を明示的に設定することで、構造体を共用体と同じように扱ってきました。 しかし、共用体は使う場面が限られていて、たとえ使った場合でも見つけにくいバグを生じてしまうおそれがあるということは、CLSに共用体という概念が存在しないことからもいえることではないかと思います。 しかし、適切な場面で使えば共用体的な概念は有効でバグを減らす可能性も秘めています。
 

        

        
~
**フィールドのオフセットの取得 (Marshal.OffsetOf)
次のサンプルは.NET FrameworkのSystem.Drawing名前空間にも存在する&msdn(netfx,type,System.Drawing.dll,System.Drawing.Color){Color構造体};やRGBQUAD構造体に似た構造体を独自に定義したものです。
+
C/C++の``offsetof()``のようにフィールドのオフセットを取得したい場合は&msdn(netfx,member,System.Runtime.InteropServices.Marshal.OffsetOf){Marshal.OffsetOf};メソッドを使用します。 このメソッドはFieldOffset属性で明示的にオフセットを指定していないフィールドでもオフセットを取得することができるため、実行時までオフセットがわからないフィールドのオフセットも取得することができます。
 

        

        
~
Marshal.OffsetOfメソッドではオフセットを取得したい型を&msdn(netfx,type,System.Type){Type型};、フィールド名を文字列で指定します。 オフセットは&msdn(netfx,type,System.IntPtr){IntPtr型};で返されます。
#tabpage(codelang=cs)
+

          
+
#tabpage(codelang=cs,container-title=フィールドのオフセットの取得)
 
#code{{
#code{{
 
using System;
using System;
 
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
 

        

        
 
[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Explicit)]
~
struct S1 {
struct Color
~
  [FieldOffset(4)] int F1; // オフセットを4に設定したフィールド
{
~
}
  [FieldOffset(0)] public uint Value;
 

        

        
~
[StructLayout(LayoutKind.Sequential)]
  [FieldOffset(0)] public byte R;
~
struct S2 {
  [FieldOffset(1)] public byte G;
~
  byte F1;
  [FieldOffset(2)] public byte B;
~
  int  F2; // 1バイトのフィールドの後ろに配置されるフィールド
  [FieldOffset(3)] public byte A;
 
}
}
 

        

        
~
// フィールドの型と数はS2と同じだが、アラインメントを1バイトに指定した構造体
class SampleClass
~
[StructLayout(LayoutKind.Sequential, Pack = 1)]
{
+
struct S3 {
+
  byte F1;
+
  int  F2; // 1バイトのフィールドの後ろに配置されるフィールド
+
}
+

          
+
class Sample {
 
  static void Main()
  static void Main()
 
  {
  {
~
    // 構造体S1のフィールドF1のオフセットを取得する
    Color c = new Color();
+
    Console.WriteLine("OffsetOf(S1.F1) = {0}", Marshal.OffsetOf(typeof(S1), "F1").ToInt32());
 

        

        
~
    // 構造体S2のフィールドF2のオフセットを取得する
    // 色の値を4バイトの整数で指定
~
    Console.WriteLine("OffsetOf(S2.F2) = {0}", Marshal.OffsetOf(typeof(S2), "F2").ToInt32());
    c.Value = 0x80e0c0a0;
 

        

        
~
    // 構造体S3のフィールドF2のオフセットを取得する
    // 各色要素の値表示を表示
~
    Console.WriteLine("OffsetOf(S3.F2) = {0}", Marshal.OffsetOf(typeof(S3), "F2").ToInt32());
    Console.WriteLine("Alpha: 0x" + c.A.ToString("X2"));
-
    Console.WriteLine("R: 0x" + c.R.ToString("X2"));
-
    Console.WriteLine("G: 0x" + c.G.ToString("X2"));
-
    Console.WriteLine("B: 0x" + c.B.ToString("X2"));
-

          
-
    // RGBAの各値を個別に指定
-
    c.A = 0x40;
-
    c.R = 0xff;
-
    c.G = 0x80;
-
    c.B = 0x00;
-

          
-
    Console.WriteLine("Value: 0x" + c.Value.ToString("X4"));
 
  }
  }
 
}
}
 
}}
}}
465,410 362,173
 
Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices
 

        

        
 
<StructLayout(LayoutKind.Explicit)> _
<StructLayout(LayoutKind.Explicit)> _
~
Structure S1
Structure Color
~
  <FieldOffset(4)> Dim F1 As Integer ' オフセットを4に設定したフィールド
  <FieldOffset(0)> Public Value As UInteger
+
End Structure
+

          
+
<StructLayout(LayoutKind.Sequential)> _
+
Structure S2
+
  Dim F1 As Byte
+
  Dim F2 As Integer ' 1バイトのフィールドの後ろに配置されるフィールド
+
End Structure
+

          
+
' フィールドの型と数はS2と同じだが、アラインメントを1バイトに指定した構造体
+
<StructLayout(LayoutKind.Sequential, Pack := 1)> _
+
Structure S3
+
  Dim F1 As Byte
+
  Dim F2 As Integer ' 1バイトのフィールドの後ろに配置されるフィールド
+
End Structure
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    ' 構造体S1のフィールドF1のオフセットを取得する
+
    Console.WriteLine("OffsetOf(S1.F1) = {0}", Marshal.OffsetOf(GetType(S1), "F1").ToInt32())
 

        

        
~
    ' 構造体S2のフィールドF2のオフセットを取得する
  <FieldOffset(0)> Public R As Byte
~
    Console.WriteLine("OffsetOf(S2.F2) = {0}", Marshal.OffsetOf(GetType(S2), "F2").ToInt32())
  <FieldOffset(1)> Public G As Byte
-
  <FieldOffset(2)> Public B As Byte
-
  <FieldOffset(3)> Public A As Byte
-
End Structure
-

          
-
Class SampleClass
-
  Shared Sub Main()
-
    Dim c As New Color()
-

          
-
    ' 色の値を4バイトの整数で指定
-
    c.Value = &h80e0c0a0
-

          
-
    ' 各色要素の値表示を表示
-
    Console.WriteLine("Alpha: 0x" + c.A.ToString("X2"))
-
    Console.WriteLine("R: 0x" + c.R.ToString("X2"))
-
    Console.WriteLine("G: 0x" + c.G.ToString("X2"))
-
    Console.WriteLine("B: 0x" + c.B.ToString("X2"))
-

          
-
    ' RGBAの各値を個別に指定
-
    c.A = &h40
-
    c.R = &hff
-
    c.G = &h80
-
    c.B = &h00
 

        

        
~
    ' 構造体S3のフィールドF2のオフセットを取得する
    Console.WriteLine("Value: 0x" + c.Value.ToString("X4"))
+
    Console.WriteLine("OffsetOf(S3.F2) = {0}", Marshal.OffsetOf(GetType(S3), "F2").ToInt32())
 
  End Sub
  End Sub
 
End Class
End Class
 
}}
}}
 
#tabpage-end
#tabpage-end
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
OffsetOf(S1.F1) = 4
Alpha: 0x80
~
OffsetOf(S2.F2) = 4
R: 0xA0
~
OffsetOf(S3.F2) = 1
G: 0xC0
-
B: 0xE0
-
Value: 0x400080FF
-
Press any key to continue
 
}}
}}
 

        

        
~
この結果からもわかるように、アラインメントによってフィールドのオフセットが異なる場合もあります。 アラインメントの指定については後述の[[#StructLayoutAttribute.Pack]]を参照してください。
Color構造体はA・R・G・Bの四つの独立した値を持つことができます。 この値は、場合によっては4バイトの整数として取得または設定したいということもあります。
+

          
+
.NET Framework 2.0以降では、非パブリックフィールドのオフセットも取得することができます。
+

          
 

        

        
+
*アラインメントの指定 (StructLayoutAttribute.Pack) [#StructLayoutAttribute.Pack]
+
構造体(またはクラス)のフィールドのアラインメント(パッキングサイズ)を変更するにはStructLayout属性で&msdn(netfx,member,System.Runtime.InteropServices.StructLayoutAttribute.Pack){Packフィールド};を指定します。 ``Pack``を指定した場合、各フィールドはPackで指定された値の倍数のオフセットに配置されます。 例えば2を指定すれば各フィールドのオフセットは2の倍数となります。 ``Pack``に指定できる値は0, 1, 2, 4, 8, 16, 32, 64, 128のいずれかです。 0を指定した場合は、デフォルトと同じ、つまり``Pack``を指定しなかった場合と同じになります。
+

          
+
#tabpage(codelang=cs,container-title=アラインメントの指定)
+
#code{{
+
using System;
+
using System.Runtime.InteropServices;
+

          
+
// デフォルトのアラインメント
+
struct S1 {
+
  byte F1;
+
  int  F2;
+
  int  F3;
+
}
+

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

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

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

          
+
class Sample {
+
  static void Main()
+
  {
+
    // 各構造体のフィールドのオフセットを表示する
+
    foreach (var type in new[] {typeof(S1), typeof(S2), typeof(S3), typeof(S4)}) {
+
      foreach (var field in new[] {"F1", "F2", "F3"}) {
+
        Console.WriteLine("OffsetOf({0}.{1}) = {2}", type, field, Marshal.OffsetOf(type, field).ToInt32());
+
      }
+
      Console.WriteLine();
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Runtime.InteropServices
+

          
+
' デフォルトのアラインメント
+
Structure S1
+
  Dim F1 As Byte
+
  Dim F2 As Integer
+
  Dim F3 As Integer
+
End Structure
+

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

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

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

          
+
Class Sample
+
  Public Shared Sub Main()
+
    ' 各構造体のフィールドのオフセットを表示する
+
    For Each type As Type In New Type() {GetType(S1), GetType(S2), GetType(S3), GetType(S4)}
+
      For Each field As String In New String() {"F1", "F2", "F3"}
+
        Console.WriteLine("OffsetOf({0}.{1}) = {2}", type, field, Marshal.OffsetOf(type, field).ToInt32())
+
      Next
+
      Console.WriteLine()
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
OffsetOf(S1.F1) = 0
+
OffsetOf(S1.F2) = 4
+
OffsetOf(S1.F3) = 8
+

          
+
OffsetOf(S2.F1) = 0
+
OffsetOf(S2.F2) = 1
+
OffsetOf(S2.F3) = 5
+

          
+
OffsetOf(S3.F1) = 0
+
OffsetOf(S3.F2) = 2
+
OffsetOf(S3.F3) = 6
+

          
+
OffsetOf(S4.F1) = 0
+
OffsetOf(S4.F2) = 4
+
OffsetOf(S4.F3) = 8
+
}}
+

          
+
この結果からもわかるとおり、各フィールドは次のように配置されているはずです。
+

          
+
#column
 
#html{{
#html{{
 
<table>
<table>
~
  <caption>構造体S2(Pack=1)の各フィールドの配置</caption>
  <caption>Color構造体のレイアウト</caption>
 
  <thead>
  <thead>
 
    <tr>
    <tr>
~
      <th>オフセット<br/>(バイト)</th>
      <th>オフセット値<br/>(バイト)</th>
-
      <th>フィールド</th>
 
      <th>フィールド</th>
      <th>フィールド</th>
 
    </tr>
    </tr>
 
  </thead>
  </thead>
 
  <tbody>
  <tbody>
 
    <tr>
    <tr>
 
      <td>0</td>
      <td>0</td>
~
      <td><code>byte F1</code></td>
      <td style="background-color: #fbb;">R</td>
-
      <td rowspan="4">Value</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>1</td>
      <td>1</td>
~
      <td rowspan="4"><code>int F2</code></td>
      <td style="background-color: #bfb;">G</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>2</td>
      <td>2</td>
-
      <td style="background-color: #bbf;">B</td>
 
    </tr>
    </tr>
 
    <tr>
    <tr>
 
      <td>3</td>
      <td>3</td>
~
    </tr>
      <td style="background-color: #bbb;">A</td>
+
    <tr>
+
      <td>4</td>
+
    </tr>
+
    <tr>
+
      <td>5</td>
+
      <td rowspan="4"><code>int F3</code></td>
+
    </tr>
+
    <tr>
+
      <td>6</td>
+
    </tr>
+
    <tr>
+
      <td>7</td>
+
    </tr>
+
    <tr>
+
      <td>8</td>
+
    </tr>
+
  </tbody>
+
</table>
+
}}
+
#column
+
#html{{
+
<table>
+
  <caption>構造体S3(Pack=2)の各フィールドの配置</caption>
+
  <thead>
+
    <tr>
+
      <th>オフセット<br/>(バイト)</th>
+
      <th>フィールド</th>
+
    </tr>
+
  </thead>
+
  <tbody>
+
    <tr>
+
      <td style="border-bottom-style: dashed;">0</td>
+
      <td><code>byte F1</code></td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed;">1</td>
+
      <td style="background-color: lightgray;">未使用</td>
+
    </tr>
+
    <tr>
+
      <td style="border-bottom-style: dashed;">2</td>
+
      <td rowspan="4"><code>int F2</code></td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed;">3</td>
+
    </tr>
+
    <tr>
+
      <td style="border-bottom-style: dashed;">4</td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed;">5</td>
+
    </tr>
+
    <tr>
+
      <td style="border-bottom-style: dashed;">6</td>
+
      <td rowspan="4"><code>int F3</code></td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed;">7</td>
+
    </tr>
+
    <tr>
+
      <td style="border-bottom-style: dashed;">8</td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed;">9</td>
+
    </tr>
+
  </tbody>
+
</table>
+
}}
+
#column
+
#html{{
+
<table>
+
  <caption>構造体S4(Pack=4)の各フィールドの配置</caption>
+
  <thead>
+
    <tr>
+
      <th>オフセット<br/>(バイト)</th>
+
      <th>フィールド</th>
+
    </tr>
+
  </thead>
+
  <tbody>
+
    <tr>
+
      <td style="border-bottom-style: dashed;">0</td>
+
      <td><code>byte F1</code></td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed; border-bottom-style: dashed;">1</td>
+
      <td rowspan="3" style="background-color: lightgray;">未使用</td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed; border-bottom-style: dashed;">2</td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed;">3</td>
+
    </tr>
+
    <tr>
+
      <td style="border-bottom-style: dashed;">4</td>
+
      <td rowspan="4"><code>int F2</code></td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed; border-bottom-style: dashed;">5</td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed; border-bottom-style: dashed;">6</td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed;">7</td>
+
    </tr>
+
    <tr>
+
      <td style="border-bottom-style: dashed;">8</td>
+
      <td rowspan="4"><code>int F3</code></td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed; border-bottom-style: dashed;">9</td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed; border-bottom-style: dashed;">10</td>
+
    </tr>
+
    <tr>
+
      <td style="border-top-style: dashed;">11</td>
 
    </tr>
    </tr>
 
  </tbody>
  </tbody>
 
</table>
</table>
 
}}
}}
+
#column-end
 

        

        
~
なお、.NET FrameworkおよびMonoのデフォルトではアラインメントは``4``となるようです。
このように、一つの型でありながら、複数の表現方法があるときなどには共用体の概念は非常に有効です。 StructLayout属性とFieldOffset属性を使用することで、構造体によって共用体と同様の機能を得ると同時に、共用体以上の機能を持った構造体を作ることも可能です。
 

        

        
-
最後に、先ほどのColor構造体をプロパティによってプログラムしたサンプルを載せておきます。 同じ機能を実現する場合において、その違いがよくわかると思います。
 

        

        
~
*サイズの指定 (StructLayoutAttribute.Size) [#StructLayoutAttribute.Size]
#code(cs){{
~
StructLayout属性で&msdn(netfx,member,System.Runtime.InteropServices.StructLayoutAttribute.Size){Sizeフィールド};を指定することにより、構造体のサイズを明示的に指定することができます。 例えばサイズは固定されているが、具体的なフィールドは割り当てられていない構造体を作成したい場合などに使用します。 [[Packフィールド>#StructLayoutAttribute.Pack]]とは異なり、``Size``フィールドには2&sup{n};以外の値を指定することができます。
using System;
 

        

        
~
#tabpage(codelang=cs,container-title=サイズの指定)
struct Color
~
#code{{
{
~
using System;
  private uint val;
+
using System.Runtime.InteropServices;
 

        

        
~
// デフォルトのサイズ
  private byte r;
~
struct S1 {
  private byte g;
~
  byte F1;
  private byte b;
~
}
  private byte a;
-

          
-
  public uint Value {
-
    get { return val; }
-
    set
-
    {
-
      val = value;
-
      r = (byte)(val & 0xff );
-
      g = (byte)((val >>  8) & 0xff);
-
      b = (byte)((val >> 16) & 0xff);
-
      a = (byte)((val >> 24) & 0xff);
-
    }
-
  }
 

        

        
~
// 構造体のサイズに2を指定
  public byte R {
~
[StructLayout(LayoutKind.Sequential, Size = 2)]
    get { return r; }
~
struct S2 {
    set
~
  byte F1;
    {
~
}
      r = value;
-
      val = (uint)(a << 24 | b << 16 | g << 8 | r);
-
    }
-
  }
 

        

        
~
// 構造体のサイズに4を指定
  public byte G {
~
[StructLayout(LayoutKind.Sequential, Size = 4)]
    get { return g; }
~
struct S3 {
    set
~
  byte F1;
    {
~
}
      g = value;
-
      val = (uint)(a << 24 | b << 16 | g << 8 | r);
-
    }
-
  }
 

        

        
~
// 構造体のサイズに6を指定
  public byte B {
~
[StructLayout(LayoutKind.Sequential, Size = 6)]
    get { return b; }
~
struct S4 {
    set
~
  byte F1;
    {
~
}
      b = value;
-
      val = (uint)(a << 24 | b << 16 | g << 8 | r);
-
    }
-
  }
 

        

        
~
class Sample {
  public byte A {
~
  static void Main()
    get { return a; }
~
  {
    set
~
    // 各構造体のサイズを表示する
    {
~
    foreach (var type in new[] {typeof(S1), typeof(S2), typeof(S3), typeof(S4)}) {
      a = value;
~
      Console.WriteLine("SizeOf({0}) = {1}", type, Marshal.SizeOf(type));
      val = (uint)(a << 24 | b << 16 | g << 8 | r);
 
    }
    }
 
  }
  }
 
}
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Runtime.InteropServices
 

        

        
~
' デフォルトのサイズ
class SampleClass
~
Structure S1
{
~
  Dim F1 As Byte
  static void Main()
~
End Structure
  {
-
    Color c = new Color();
 

        

        
~
' 構造体のサイズに2を指定
    // 色の値を4バイトの整数で指定
~
<StructLayout(LayoutKind.Sequential, Size := 2)> _
    c.Value = 0x80e0c0a0;
+
Structure S2
+
  Dim F1 As Byte
+
End Structure
 

        

        
~
' 構造体のサイズに4を指定
    // 各色要素の値表示を表示
~
<StructLayout(LayoutKind.Sequential, Size := 4)> _
    Console.WriteLine("Alpha: 0x" + c.A.ToString("X2"));
~
Structure S3
    Console.WriteLine("R: 0x" + c.R.ToString("X2"));
~
  Dim F1 As Byte
    Console.WriteLine("G: 0x" + c.G.ToString("X2"));
~
End Structure
    Console.WriteLine("B: 0x" + c.B.ToString("X2"));
-

          
-
    // RGBAの各値を個別に指定
-
    c.A = 0x40;
-
    c.R = 0xff;
-
    c.G = 0x80;
-
    c.B = 0x00;
 

        

        
~
' 構造体のサイズに6を指定
    Console.WriteLine("Value: 0x" + c.Value.ToString("X4"));
~
<StructLayout(LayoutKind.Sequential, Size := 6)> _
  }
~
Structure S4
}
+
  Dim F1 As Byte
+
End Structure
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    ' 各構造体のサイズを表示する
+
    For Each type As Type In New Type() {GetType(S1), GetType(S2), GetType(S3), GetType(S4)}
+
      Console.WriteLine("SizeOf({0}) = {1}", type, Marshal.SizeOf(type))
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
SizeOf(S1) = 1
+
SizeOf(S2) = 2
+
SizeOf(S3) = 4
+
SizeOf(S4) = 6
 
}}
}}
 

        

        
+
構造体のサイズの取得については[[programming/netfx/struct/2_sizeof]]も参照してください。
+

          
 
#navi(..)
#navi(..)