2014-08-24T22:30:19の更新内容

programming/vb.net/structure/index.wiki.txt

current previous
2,7 2,9
 
${smdncms:header_title,構造体 (Structure)}
${smdncms:header_title,構造体 (Structure)}
 
${smdncms:keywords,構造体,Structure,ユーザー定義型,New,ReadOnly,WriteOnly,初期化,再初期化,ゼロクリア}
${smdncms:keywords,構造体,Structure,ユーザー定義型,New,ReadOnly,WriteOnly,初期化,再初期化,ゼロクリア}
 

        

        
~
構造体とは既存のデータ型を組み合わせた構造を持つ型のことです。 構造体では複数の値(データ型)を組み合わせることにより、それらに意味を持たせた一つの型を作成することができます。 VB6以前でユーザー定義型と呼ばれていたものは、VB.NETでは''構造体''という名称に変わり、[[クラス>programming/vb.net/class]]と同様に構造体が[[プロシージャ(メソッド)>programming/vb.net/procedure]]を持つことができるようになっているなど、機能も大幅に強化されています。
構造体とは基本データ型をいくつか組み合わせた構造を持った型のことです。 VB6以前でのユーザー定義型は、VB.NETからは構造体という名称に変わり、機能も追加されています。
-

          
-
なお、構造体内に配列を宣言する場合の注意点などについては[[programming/netfx/arrays/3_structfields]]で個別に詳しく解説しているので合わせて参照してください。
 

        

        
 
-関連するページ
-関連するページ
 
--[[programming/vb.net/class]]
--[[programming/vb.net/class]]
14,44 16,36
 

        

        
 
#googleadunit(banner)
#googleadunit(banner)
 

        

        
~
*構造体の宣言 [#declaration]
*構造体の宣言
~
まずは構造体の宣言について、その構文を見てみます。 ここでは長方形の座標を扱う&msdn(netfx,id,a5ch4fda){RECT構造体};に相当する構造体を例に挙げています。 比較のため、同等の型をVB6のユーザー定義型、C言語の構造体として宣言した場合についても併記しています。
VB.NETでの構造体の仕様はVB6のユーザー定義型よりもC++の構造体に近くなっています。 また、宣言様式も変わっているのでまずはその構文を見てみましょう。
 

        

        
~
#column
#code(bas,VB6以前 でのユーザー定義型の宣言){{
+
#code(vb,VB.NETでの構造体の宣言){{
+
Structure Rect
+
  Dim Left As Integer
+
  Dim Top As Integer
+
  Dim Right As Integer
+
  Dim Bottom As Integer
+
End Structure
+
}}
+
#column
+
#code(bas,VB6以前でのユーザー定義型の宣言){{
 
Type RECT
Type RECT
~
  Left As Long

          
~
  Top As Long
    Left As Long
~
  Right As Long
    Top As Long
~
  Bottom As Long
    Right As Long
-
    Bottom As Long
-

          
 
End Type
End Type
 
}}
}}
~
#column

          
~
#code(c,C言語での構造体の宣言){{
#code(vb,VB.NET での構造体の宣言){{
~
struct RECT
Structure Rect
~
{

          
~
  long Left;
    Dim Left As Integer
~
  long Top;
    Dim Top As Integer
~
  long Right;
    Dim Right As Integer
~
  long Bottom;
    Dim Bottom As Integer
~
};

          
-
End Structure
 
}}
}}
+
#column-end
 

        

        
~
(上記のコードについて、VB6以前では``Long``が32ビット、VB.NETでは``Integer``が[[32ビットの整数を表す>programming/vb.net/local_variable_declaration]]という違いがあります。)
この構造体は長方形座標を扱うRECT構造体のVB.NET版の一例です。 参考までにこれとほぼ等価なVB6でのコードを記述しておきました。
 

        

        
~
VB.NETでは``Type``〜``End Type``ではなく、``Structure``〜``End Structure``を用いて構造体を宣言します。 また、VB.NETの構造体ではフィールド(構造体内の変数・メンバ変数)やプロシージャ(メソッド)を含むすべてのメンバに[[アクセシビリティ>programming/vb.net/accessibility]]を設定できるようになっています。 そのためメンバは``Dim``または``Public``や``Private``などの''アクセス修飾子''を用いて宣言します。 なお、``Dim``で宣言した構造体のメンバは``Public``と同じアクセス範囲となります。 そのため、上記の例にあるフィールド(構造体内の変数)はすべて``Public``となります。
VB.NETではType〜End Typeではなく、Structure〜End Structureを用いて構造体を宣言します。 また、フィールド・メソッドを含めて構造体のすべてのメンバに[[アクセシビリティ>programming/vb.net/accessibility]]を設定できるようになりました。 このためメンバはDimやPublic、Privateを用いて宣言します。 Dimで宣言した構造体のメンバはPublic扱いとなります。 そのため、上記の例にあるフィールド(構造体内の変数)はすべてPublicとなります。
 

        

        
~
このように宣言した構造体を実際に使う場合は次のようになります。 ``構造体変数.メンバ名``の形式で構造体内の各メンバにアクセスできるようになっています。 このように、使用する場合の構文についてはVB6と概ね変わりありません。
このように宣言した構造体を実際に使う場合の構文についてはVB6とかわりありません。
 

        

        
 
#code(vb){{
#code(vb){{
 
' 構造体型変数の宣言
' 構造体型変数の宣言
64,145 58,125
 
r.Bottom = 240
r.Bottom = 240
 
}}
}}
 

        

        
~
``With``ステートメントを使用することにより、メンバの参照時に構造体変数の記述を省略することもできます。
ちなみに、長方形の座標を扱う構造体はSystem.Drawing名前空間にRectangle型として既に.NET Frameworkに用意されています。 また、命名基準についてなのですが、VB.NETでは構造体でもすべて大文字からなる名前は付けないようにガイドラインで定められています。 ただ、あくまで指針であるので、わかりやすさが維持される限りは自由に名前を付けることができます。
-

          
-
*メソッド
-
VB.NETの構造体はC++並の仕様をそなえているので、ほとんどクラスと同等の機能を持つことができます。 ただ、[[programming/vb.net/class]]でも解説しているとおり、VB.NETにおけるクラスと構造体は明確な差があるので、使い分ける必要があります。
-

          
-
話を構造体の仕様に戻して、どんな機能が追加されたかというと、VB.NETの構造体ではメソッドを持つことができるようになりました。 ひとまず次のサンプルを見てください。 このサンプルでは先ほどのRect構造体に値を設定するためのメソッドをそなえています。
 

        

        
 
#code(vb){{
#code(vb){{
~
' 構造体型変数の宣言
Structure Rect
+
Dim r As Rect
 

        

        
~
' 変数rの記述を省略してメンバを参照する
    Dim Left As Integer
~
With r
    Dim Top As Integer
~
  .Left = 0
    Dim Right As Integer
~
  .Top = 0
    Dim Bottom As Integer
~
  .Right = 320

          
~
  .Bottom = 240
    Sub SetRect(ByVal left As Integer, ByVal top As Integer, ByVal right As Integer, ByVal bottom As Integer)
~
End With

          
~
}}
        Me.Left = left
-
        Me.Top = top
-
        Me.Right = right
-
        Me.Bottom = bottom
 

        

        
-
    End Sub
 

        

        
~
構造体を宣言する際の命名基準について、VB.NETでは構造体の場合もクラスと同様の命名方法、つまりすべて大文字からなる名前は付けないようにガイドラインで定められています。 (例えば、``RECT``よりは``Rect``が推奨される) ただ、あくまで指針であるので、わかりやすさが維持される限りは自由に名前を付けることができます。
End Structure
 

        

        
~
ちなみに、長方形の座標を扱う構造体はSystem.Drawing名前空間に&msdn(netfx,type,System.Drawing.dll,System.Drawing.Rectangle){Rectangle構造体};として既に.NET Frameworkに用意されています。 そのため、Rectangle構造体が使用できるのであれば上記のような構造体をわざわざ独自に宣言する必要はありません。
Module MainModule
 

        

        
-
    Sub Main()
 

        

        
~
*メソッド [#method]
        ' 構造体型変数の宣言
~
VB.NETでの構造体の仕様はVB6のユーザー定義型よりもC++の構造体に近くなっていて、[[クラス>programming/vb.net/class]]に近い機能を持っています。 その一つとして、VB.NETの構造体は[[メソッド>programming/vb.net/procedure]]を持つことができるようになっています。
        Dim r As Rect
 

        

        
~
例えば次のサンプルでは、先ほどのRect構造体に対してメンバの値を設定するためのメソッドを追加しています。
        ' 各メンバに値を設定する
-
        r.SetRect(0, 0, 320, 240)
 

        

        
~
#code(vb){{
    End Sub
+
Structure Rect
+
  Dim Left As Integer
+
  Dim Top As Integer
+
  Dim Right As Integer
+
  Dim Bottom As Integer
+

          
+
  ' 構造体の各メンバ変数に対して一度に値を設定するためのメソッド
+
  Sub SetRect(ByVal left As Integer, ByVal top As Integer, ByVal right As Integer, ByVal bottom As Integer)
+
    Me.Left = left
+
    Me.Top = top
+
    Me.Right = right
+
    Me.Bottom = bottom
+
  End Sub
+
End Structure
 

        

        
+
Module Sample
+
  Sub Main()
+
    ' 構造体型変数の宣言
+
    Dim r As Rect
+

          
+
    ' メソッドを使って各メンバに値を設定する
+
    r.SetRect(0, 0, 320, 240)
+
  End Sub
 
End Module
End Module
 
}}
}}
 

        

        
~
このように、VB.NETでは構造体にメソッドを持たせることができます。 メソッド以外にも、[[コンストラクタ>#constructor]]や[[プロパティ>#property]]を構造体に追加することもできます。
このように、VB.NETでは構造体にメソッドを持たせることができます。 上記の例における[[Meキーワード>programming/vb.net/me_myclass_mybase#MeMyClassKeyword]]は、構造体自身がメンバを参照するためのキーワードで、たとえば引数のleftとメンバのLeftを区別するために用いています。 区別する必要がなければ次のようにMeキーワードを省略することもできます。
+

          
+
なお、上記の例における[[Meキーワード>programming/vb.net/me_myclass_mybase#MeMyClassKeyword]]は、構造体自身がメンバを参照するためのキーワードです。 このメソッドでの``Me``は、メソッドの引数``left``と構造体メンバの``Left``を区別するために用いています。 このような区別をする必要がなければ``Me``は不要です。 例えば次のように構造体メンバとは異なる引数名にすれば``Me``キーワードは不要となり、わざわざ明示する必要はなくなります。
 

        

        
 
#code(vb){{
#code(vb){{
 
Sub SetRect(ByVal l As Integer, ByVal t As Integer, ByVal r As Integer, ByVal b As Integer)
Sub SetRect(ByVal l As Integer, ByVal t As Integer, ByVal r As Integer, ByVal b As Integer)
~
  Left = l

          
~
  Top = t
    Left = l
~
  Right = r
    Top = t
~
  Bottom = b
    Right = r
-
    Bottom = b
-

          
 
End Sub
End Sub
 
}}
}}
 

        

        
~
値を返さないメソッドである``Sub``プロシージャだけでなく、当然値を返すメソッドである``Function``プロシージャを構造体に持たせることも可能です。
また、値を返すFunctionメソッドを持たせることも可能です。
 

        

        
 
#code(vb){{
#code(vb){{
 
' 長方形の幅を返すメソッド
' 長方形の幅を返すメソッド
 
Function GetWidth() As Integer
Function GetWidth() As Integer
~
  Return Right - Left

          
-
    Return Right - Left
-

          
 
End Function
End Function
 

        

        
 
' 長方形の高さを返すメソッド
' 長方形の高さを返すメソッド
 
Function GetHeight() As Integer
Function GetHeight() As Integer
~
  Return Bottom - Top

          
-
    Return Bottom - Top
-

          
 
End Function
End Function
 
}}
}}
 

        

        
~
ただ、この場合はメソッドよりも[[プロパティ>#property]]にした方が設計的には優れているかもしれません。 [[プロパティについては後述します>#property]]。
ただ、この場合はメソッドではなくプロパティにした方が設計的には優れているかもしれません。 プロパティについては後述します。
+

          
 

        

        
~
*構造体とクラスの共通点・相違点 [#structure_and_class]
*コンストラクタ (初期値の設定)
~
構造体とクラスは多くの共通する機能を持っていますが、一方で[[複製>programming/netfx/cloning]]時のコストなど実行時のパフォーマンスに影響するような大きな違いもあります([[programming/netfx/valuetype_referencetype]])。 そういった違いを意識せずにクラスと構造体の選択を安易に行うと、[[一見すると不可思議なエラー>programming/netfx/collections/2_generic_1_list#modify_valuetype_element]]に遭遇する場合もあります。 そのため、機能は似ていたとしても構造体とクラスは適切に使い分ける必要があります。
構造体にNewという名前のメソッドを作成すると、それはコンストラクタとして扱われます。 コンストラクタは構造体変数が宣言されたときに呼び出される特殊なメソッドで、主にメンバ変数の初期化を行います。 ただし、クラスのコンストラクタとは異なり、構造体におけるコンストラクタは必ず1つ以上の引数を取る必要があります。 引数のないコンストラクタは暗黙的に作成されています。
+

          
+
構造体とクラスの共通点・相違点に関しては[[programming/vb.net/class]]での解説も合わせて参照してください。
+

          
+

          
+
*コンストラクタ (初期値の設定) [#constructor]
+
構造体に``New``という名前のメソッドを作成すると、それは''コンストラクタ''として扱われます。 コンストラクタは``New``ステートメントで構造体変数を宣言するときに呼び出される特殊なメソッドで、主にメンバ変数の初期化を行うために用います。
 

        

        
 
次のコードは先ほどのRect構造体に、宣言時に初期化できるようなコンストラクタを追加した例です。
次のコードは先ほどのRect構造体に、宣言時に初期化できるようなコンストラクタを追加した例です。
 

        

        
 
#code(vb){{
#code(vb){{
 
Structure Rect
Structure Rect
+
  Dim Left As Integer
+
  Dim Top As Integer
+
  Dim Right As Integer
+
  Dim Bottom As Integer
 

        

        
~
  ' コンストラクタ
    Dim Left As Integer
~
  Sub New(ByVal l As Integer, ByVal t As Integer, ByVal r As Integer, ByVal b As Integer)
    Dim Top As Integer
~
    Left = l
    Dim Right As Integer
~
    Top = t
    Dim Bottom As Integer
~
    Right = r

          
~
    Bottom = b
    Sub New(ByVal l As Integer, ByVal t As Integer, ByVal r As Integer, ByVal b As Integer)
~
  End Sub

          
-
        Left = l
-
        Top = t
-
        Right = r
-
        Bottom = b
-

          
-
    End Sub
-

          
-
     :
-
    以下同じ
-
     :
-
     :
 

        

        
+
  ' 通常のメソッド
+
  Sub SetRect(ByVal l As Integer, ByVal t As Integer, ByVal r As Integer, ByVal b As Integer)
+
    Left = l
+
    Top = t
+
    Right = r
+
    Bottom = b
+
  End Sub
 
End Structure
End Structure
 
}}
}}
 

        

        
~
変数宣言時にこのコンストラクタを使用する場合は次のように``New``キーワードを使用します。
そして、変数宣言時にこのコンストラクタを使用する場合は次のようにNewキーワードを使用します。
 

        

        
 
#code(vb){{
#code(vb){{
 
' コンストラクタを使用した例
' コンストラクタを使用した例
 
Dim r As New Rect(160, 120, 640, 480)
Dim r As New Rect(160, 120, 640, 480)
 

        

        
~
' 次のコードは上のコードと同等で、同じ結果となります
' 次のコードは上のコードと等価です
 
Dim r As Rect
Dim r As Rect
+

          
 
r.SetRect(160, 120, 640, 480)
r.SetRect(160, 120, 640, 480)
 
}}
}}
 

        

        
~
クラスのコンストラクタとは異なり、構造体におけるコンストラクタは常に''1つ以上の引数を取る必要があります''。 構造体においては、引数のないコンストラクタは暗黙のコンストラクタとして自動的に用意されるため、そのようなコンストラクタを作成することはできません。
コンストラクタをもつ構造体でも、コンストラクタを呼び出さずにNewキーワードを使用して初期化することが出来ます。 この場合、暗黙のコンストラクタが呼び出され、構造体内の各フィールドは0など(参照型ならNothing)のデフォルト値に初期化されます。 (型とそのデフォルト値については[[programming/netfx/basic_types/0_characteristics#DefaultValue]]を参照してください)
+

          
+
コンストラクタをもつ構造体の場合でも、作成したコンストラクタを使用せず初期化することが出来ます。 この場合、暗黙のコンストラクタが呼び出されます。 ここまでの例で明らかなように、必要がなければコンストラクタを用意しなくてもよく、またコンストラクタが無くても暗黙のコンストラクタによって構造体のインスタンスを作成することができます。
+

          
+
なお、暗黙のコンストラクタが呼び出される場合、構造体内の各フィールドは0など(参照型ならNothing)のデフォルト値に初期化されます。 (型とそのデフォルト値については[[programming/netfx/basic_types/0_characteristics#DefaultValue]]を参照してください)
 

        

        
 
#code(vb){{
#code(vb){{
~
' コンストラクタを使用しない(暗黙のコンストラクタによって初期化する)例
' コンストラクタを使用しない例
 
Dim r As New Rect
Dim r As New Rect
 

        

        
 
Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
212,21 186,14
 
(0, 0)-(0, 0)
(0, 0)-(0, 0)
 
}}
}}
 

        

        
~
``New``キーワードを使用せずに単に変数として宣言した場合もこれと同じ動作となります。 言い換えると、明示的にコンストラクタを呼び出さない場合は、変数として宣言した時点で暗黙のコンストラクタにより初期化されるとも言えます。
Newキーワードを使用せずに単に変数として宣言した場合もこれと同じ動作となります。 言い換えると、明示的にコンストラクタを呼び出さない場合は、変数として宣言した時点で暗黙のコンストラクタにより初期化されるとも言えます。
 

        

        
 
#code(vb){{
#code(vb){{
+
' 構造体変数を宣言する
+
' (この際、暗黙のコンストラクタにより初期化される)
 
Dim r As Rect
Dim r As Rect
 

        

        
 
Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
 
}}
}}
 

        

        
+
#prompt(実行結果){{
+
(0, 0)-(0, 0)
+
}}
+

          
+

          
 
*オブジェクト初期化子 (初期化と値の設定)
*オブジェクト初期化子 (初期化と値の設定)
 
コンストラクタのない構造体や、暗黙のコンストラクタを使って構造体を初期化する際、''オブジェクト初期化子''を使うと特定のメンバ変数に初期値を設定することができます。 オブジェクト初期化子はVB9(VB2008)で導入されたもので、次の例のようにWithキーワードを使って構造体の初期化と値の設定を同時に行うことができます。
コンストラクタのない構造体や、暗黙のコンストラクタを使って構造体を初期化する際、''オブジェクト初期化子''を使うと特定のメンバ変数に初期値を設定することができます。 オブジェクト初期化子はVB9(VB2008)で導入されたもので、次の例のようにWithキーワードを使って構造体の初期化と値の設定を同時に行うことができます。
 

        

        
262,35 229,43
 
}}
}}
 

        

        
 
*ゼロクリア (再初期化)
*ゼロクリア (再初期化)
~
既に値が代入・設定されている構造体変数を初期化するには、``Nothing``を代入します。 ``Nothing``を代入することで、構造体内の各フィールドには``0``など(参照型なら``Nothing``)の''デフォルト値''が設定された状態になります。 これにより、構造体に対してゼロクリアに相当する再初期化を行うことが出来ます。 (型とそのデフォルト値については[[programming/netfx/basic_types/0_characteristics#DefaultValue]]を参照してください)
既に値が代入・設定されている構造体変数を初期化するには、Nothingを代入します。 Nothingを代入することで、構造体内の各フィールドには0など(参照型ならNothing)のデフォルト値が設定された状態になります。 これにより、構造体に対してゼロクリアに相当する再初期化を行うことが出来ます。 (型とそのデフォルト値については[[programming/netfx/basic_types/0_characteristics#DefaultValue]]を参照してください)
 

        

        
 
#code(vb){{
#code(vb){{
 
Structure Rect
Structure Rect
+
  Dim Left As Integer
+
  Dim Top As Integer
+
  Dim Right As Integer
+
  Dim Bottom As Integer
 

        

        
~
  Sub New(ByVal l As Integer, ByVal t As Integer, ByVal r As Integer, ByVal b As Integer)
    Dim Left As Integer
~
    Left = l
    Dim Top As Integer
~
    Top = t
    Dim Right As Integer
~
    Right = r
    Dim Bottom As Integer
~
    Bottom = b

          
~
  End Sub
    Sub New(ByVal l As Integer, ByVal t As Integer, ByVal r As Integer, ByVal b As Integer)
-

          
-
        Left = l
-
        Top = t
-
        Right = r
-
        Bottom = b
-

          
-
    End Sub
-

          
 
End Structure
End Structure
 

        

        
~
Module Sample
Module MainModule
~
  Sub Main()

          
~
    ' 構造体変数を宣言し、初期値を設定
    Sub Main()
~
    Dim r As New Rect(160, 120, 640, 480)

          
-
        ' 構造体変数を宣言し、初期値を設定
-
        Dim r As New Rect(160, 120, 640, 480)
-

          
-
        Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
 

        

        
~
    Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
        ' Nothing(構造体の初期値)を代入して変数を再初期化
-
        r = Nothing
 

        

        
~
    ' Nothing(構造体の初期値)を代入して変数を再初期化
        Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
~
    r = Nothing

          
-
    End Sub
 

        

        
+
    Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
+
  End Sub
 
End Module
End Module
 
}}
}}
 

        

        
299,9 274,7
 
(0, 0)-(0, 0)
(0, 0)-(0, 0)
 
}}
}}
 

        

        
~
構造体変数を``Nothing``(何も参照していない状態・ヌル参照)にすることは出来ないことから、``Nothing``を代入することで変数を再初期化するのは一見すると予想と反する動作に見える点で注意が必要です。 ``Nothing``の代入は、実際にはフィールドをすべてデフォルト値に設定するというよりは、暗黙のコンストラクタで初期化された値を代入して上書きすると言った方が適切でしょう。
構造体変数をNothing(何も参照していない状態・ヌル参照)にすることは出来ないことから、Nothingを代入することで変数を再初期化するのは一見すると予想と反する動作に見える点で注意が必要です。 Nothingの代入は、実際にはフィールドをすべてデフォルト値に設定するというよりは、暗黙のコンストラクタで初期化された値を代入して上書きすると言った方が適切でしょう。 次のように暗黙のコンストラクタを使って再初期化しても結果は同じで、両者は見た目・記述の違い程度でしかないため、コーディング規約や読みやすさなどを考慮して適切な方を選ぶとよいでしょう。
+

          
+
次のように暗黙のコンストラクタを使って再初期化しても結果は同じで、両者は見た目・記述の違い程度でしかないため、コーディング規約や読みやすさなどを考慮して適切な方を選ぶとよいでしょう。
 

        

        
 
#code(vb){{
#code(vb){{
 
' 構造体変数を宣言し、初期値を設定
' 構造体変数を宣言し、初期値を設定
313,221 286,169
 
r = New Rect
r = New Rect
 
}}
}}
 

        

        
~
また、あらかじめ初期値に相当する値を定数として用意しておいて、それを代入するという方法も取れます。 .NET Frameworkでも、&msdn(netfx,member,System.Drawing.dll,System.Drawing.Rectangle.Empty){Rectangle.Empty};、&msdn(netfx,member,System.Drawing.dll,System.Drawing.Point.Empty){Point.Empty};や&msdn(netfx,member,System.Decimal.Zero){Decimal.Zero};などといったフィールドがそのような方法にも使えるよう用意されています。 ``Nothing``の代入を行うより、こういったフィールドを使用する方がコードの意図がより明確にすることができます。
また、あらかじめ初期値に相当する値を定数として用意しておいて、それを代入するという方法も取れます。 .NET Frameworkでも、&msdn(netfx,member,System.Drawing.dll,System.Drawing.Rectangle.Empty){Rectangle.Empty};、&msdn(netfx,member,System.Drawing.dll,System.Drawing.Point.Empty){Point.Empty};や&msdn(netfx,member,System.Decimal.Zero){Decimal.Zero};などといったフィールドがそのような方法にも使えるよう用意されています。
 

        

        
 
次の例は先のコードに追記してEmptyというフィールドを用意し、その値を代入することで変数を初期化しています。
次の例は先のコードに追記してEmptyというフィールドを用意し、その値を代入することで変数を初期化しています。
 

        

        
 
#code(vb){{
#code(vb){{
 
Structure Rect
Structure Rect
+
  Dim Left As Integer
+
  Dim Top As Integer
+
  Dim Right As Integer
+
  Dim Bottom As Integer
 

        

        
~
  Sub New(ByVal l As Integer, ByVal t As Integer, ByVal r As Integer, ByVal b As Integer)
    Dim Left As Integer
~
    Left = l
    Dim Top As Integer
~
    Top = t
    Dim Right As Integer
~
    Right = r
    Dim Bottom As Integer
~
    Bottom = b

          
~
  End Sub
    Sub New(ByVal l As Integer, ByVal t As Integer, ByVal r As Integer, ByVal b As Integer)
-

          
-
        Left = l
-
        Top = t
-
        Right = r
-
        Bottom = b
-

          
-
    End Sub
-

          
-
    ' 初期値となる値の定数
-
    Public Shared ReadOnly Empty As Rect = New Rect
 

        

        
+
  ' 初期値となる値の定数 (初期値で初期化された読み取り専用の値)
+
  Public Shared ReadOnly Empty As Rect = New Rect
 
End Structure
End Structure
 

        

        
~
Module Sample
Module MainModule
~
  Sub Main()

          
~
    ' 構造体変数を宣言し、初期値を設定
    Sub Main()
+
    Dim r As New Rect(160, 120, 640, 480)
 

        

        
~
    Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
        ' 構造体変数を宣言し、初期値を設定
-
        Dim r As New Rect(160, 120, 640, 480)
 

        

        
~
    ' 構造体の初期値の定数を代入して変数を初期化
        Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
~
    r = Rect.Empty

          
-
        ' 構造体の初期値の定数を代入して変数を初期化
-
        r = Rect.Empty
-

          
-
        Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
-

          
-
    End Sub
 

        

        
+
    Console.WriteLine("({0}, {1})-({2}, {3})", r.Left, r.Top, r.Right, r.Bottom)
+
  End Sub
 
End Module
End Module
 
}}
}}
 

        

        
-
*プロパティ
-
プロパティはすでにVBを使ったことがある方であれば概要は分かると思います。 ただ、VB6以前でのクラスのプロパティプロシージャの構文とは多少異なります。 プロパティ構文は次のようになっています。
 

        

        
~
*プロパティ [#property]
#code(vb){{
~
プロパティはすでにVBを使ったことがある方であれば概要は分かると思います。 プロパティは値の取得・設定をメソッド形式で行う代わりに、メンバ変数と同じ記述で行えるようにするためのものです。
Property プロパティ名() As 型
-
    Get
 

        

        
~
VB.NETにおけるプロパティプロシージャの構文はVB6以前でのものとは多少異なります。 プロパティの構文は次のようになっています。
    End Get
 

        

        
~
#column
    Set(ByVal Value As 型)
+
#code(vb,VB.NET でのプロパティの宣言){{
+
Property プロパティ名() As 型
+
  Get
+
    Return プロパティ値
+
  End Get
+

          
+
  Set(ByVal Value As 型)
+
    プロパティ値 = Value
+
  End Set
+
End Property
+
}}
+
#column
+
#code(bas,VB6以前 でのプロパティの宣言){{
+
Property Get プロパティ名() As 型
+
  プロパティ名 = プロパティ値
+
End Property
 

        

        
~
Property Let/Set プロパティ名(val As 型)
    End Set
+
  プロパティ値 = val
 
End Property
End Property
 
}}
}}
+
#column-end
 

        

        
~
VB6以前の``Property Get``、``Property Let/Set``のような別々のプロシージャではなく、``Property``キーワードで記述される''プロパティプロシージャ''の中に``Get``ブロックおよび``Set``ブロックが存在します。 また、値の設定に関して``Set``と``Let``の区別は廃止され、すべて``Set``で扱います。
VB6以前と異なり、Property Get、Property Letのように別々のプロシージャではなく、プロパティプロシージャの中にGetブロックおよびSetブロックが存在します。 また、SetとLetの区別は廃止され、すべてSetで扱います。
 

        

        
~
上記の例は読み取りおよび書き込みが可能なプロパティですが、プロパティを読み取り専用や書き込み専用にすることも可能です。 次のコードはその構文の例です。 読み取り専用のプロパティとする場合は``ReadOnly``、書き込み専用では``WriteOnly``キーワードを付加します。
この例は読み取りおよび書き込みが可能なプロパティですが、プロパティを読み取り専用や書き込み専用にすることも可能です。 次のコードはその構文の例です。 読み取り専用ではReadOnly、書き込み専用ではWriteOnlyキーワードを付加します。
 

        

        
 
#code(vb,読み取り専用プロパティ){{
#code(vb,読み取り専用プロパティ){{
 
ReadOnly Property プロパティ名() As 型
ReadOnly Property プロパティ名() As 型
~
  Get
    Get
~
    Return プロパティ値

          
~
  End Get
    End Get
 
End Property
End Property
 
}}
}}
 

        

        
 
#code(vb,書き込み専用プロパティ){{
#code(vb,書き込み専用プロパティ){{
 
WriteOnly Property プロパティ名() As 型
WriteOnly Property プロパティ名() As 型
~
  Set(ByVal Value As Integer)
    Set(ByVal Value As Integer)
~
    プロパティ値 = Value

          
~
  End Set
    End Set
 
End Property
End Property
 
}}
}}
 

        

        
 
一般的にプロパティ構文を使う場合には、次の例のようにプロパティの値を保持するための外部に公開されない内部変数を用意しておきます。 具体的な実装例は次のようになります。
一般的にプロパティ構文を使う場合には、次の例のようにプロパティの値を保持するための外部に公開されない内部変数を用意しておきます。 具体的な実装例は次のようになります。
 

        

        
 
#code(vb){{
#code(vb){{
+
' プロパティの値を保持するための内部変数
 
Private m_Width As Integer
Private m_Width As Integer
 

        

        
+
' プロパティプロシージャ
 
Property Width() As Integer
Property Width() As Integer
~
  Get
    Get
~
    ' 内部変数の値を返す
        Return m_Width
~
    Return m_Width
    End Get
~
  End Get

          
~

          
    Set(ByVal Value As Integer)
~
  Set(ByVal Value As Integer)
        m_Width = Value
~
    ' 内部変数に値を設定する
    End Set
+
    m_Width = Value
+
  End Set
 
End Property
End Property
 
}}
}}
 

        

        
~
ほかにも既存のフィールドの値を使って異なる意味と名前を与えることでプロパティとする場合もあります。 例えば先に例を挙げた[[GetWidth()およびGetHeight()メソッド>#method]]を読み取り専用プロパティとして書き換えると次のようになります。
先ほどのRect構造体のGetWidth()およびGetHeight()メソッドを読み取り専用プロパティとして書き換えると次のようになります。
 

        

        
 
#code(vb){{
#code(vb){{
+
' 長方形の幅を返すプロパティ
 
ReadOnly Property Width() As Integer
ReadOnly Property Width() As Integer
~
  Get
    Get
~
    Return Right - Left
        Return Right - Left
~
  End Get
    End Get
 
End Property
End Property
 

        

        
+
' 長方形の高さを返すプロパティ
 
ReadOnly Property Height() As Integer
ReadOnly Property Height() As Integer
~
  Get
    Get
~
    Return Bottom - Top
        Return Bottom - Top
~
  End Get
    End Get
 
End Property
End Property
 
}}
}}
 

        

        
~
次の例は、このようにして作成したプロパティを実際に使用する例です。
これらのプロパティが実際に動作するかを次のコードで試してみます。
+

          
 
#code(vb){{
#code(vb){{
 
Structure Rect
Structure Rect
+
  Dim Left As Integer
+
  Dim Top As Integer
+
  Dim Right As Integer
+
  Dim Bottom As Integer
+

          
+
  Sub SetRect(ByVal l As Integer, ByVal t As Integer, ByVal r As Integer, ByVal b As Integer)
+
    Left = l
+
    Top = t
+
    Right = r
+
    Bottom = b
+
  End Sub
 

        

        
~
  ' 読み取り専用のプロパティ
    Dim Left As Integer
~
  ReadOnly Property Width() As Integer
    Dim Top As Integer
~
    Get
    Dim Right As Integer
~
      Return Right - Left
    Dim Bottom As Integer
~
    End Get

          
~
  End Property
    Sub SetRect(ByVal l As Integer, ByVal t As Integer, _
-
                 ByVal r As Integer, ByVal b As Integer)
-

          
-
        Left = l
-
        Top = t
-
        Right = r
-
        Bottom = b
-

          
-
    End Sub
-

          
-
    ReadOnly Property Width() As Integer
-
        Get
-
            Return Right - Left
-
        End Get
-
    End Property
-

          
-
    ReadOnly Property Height() As Integer
-
        Get
-
            Return Bottom - Top
-
        End Get
-
    End Property
 

        

        
+
  ' 読み取り専用のプロパティ
+
  ReadOnly Property Height() As Integer
+
    Get
+
      Return Bottom - Top
+
    End Get
+
  End Property
 
End Structure
End Structure
 

        

        
~
Module Sample
Module MainModule
+
  Sub Main()
+
    ' 構造体型変数の宣言
+
    Dim r As Rect
+

          
+
    ' 各メンバに値を設定する
+
    r.SetRect(160, 120, 640, 480)
+

          
+
    ' プロパティを使って値を取得する
+
    Console.WriteLine("Width: {0}, Height: {1}", r.Width, r.Height)
+
  End Sub
+
End Module
+
}}
 

        

        
~
#prompt(実行結果){{
    Sub Main()
+
Width: 480, Height: 360
+
}}
 

        

        
-
        ' 構造体型変数の宣言
-
        Dim r As Rect
 

        

        
~
*配列フィールド [#array_field]
        ' 各メンバに値を設定する
~
VB.NETでは構造体内に固定長の配列フィールドを作成することができません。 例えばC言語で記述された以下のような構造体をVB.NETで記述することはできません。 配列フィールドにサイズを指定しようとしてもコンパイルエラーとなります。
        r.SetRect(160, 120, 640, 480)
 

        

        
~
#column
        Console.WriteLine("Width: {0}, Height: {1}", r.Width, r.Height)
+
#code(c){{
+
struct FixedLength
+
{
+
  // サイズ16の固定長配列フィールド
+
  unsigned char Data[16];
+
};
+
}}
+
#column
+
#code(vb){{
+
Structure FixedLength
 

        

        
~
  ' error BC31043: 構造体メンバーとして宣言された配列を初期サイズで宣言することはできません。
    End Sub
+
  Public Data(15) As Byte
 

        

        
~
End Structure
End Module
 
}}
}}
+
#column-end
 

        

        
~
また、配列フィールドに初期化子を指定して割り当てを行うこともできません。 初期化子を指定しようとしてもコンパイルエラーとなります。 従って、コンストラクタを使って初期化するなどしない限り、配列フィールドは常に``Nothing``で初期化されます。
#prompt(実行結果){{
~

          
Width: 480, Height: 360
+
#code(vb){{
+
Structure FixedLength
+

          
+
  ' error BC31049: 構造体メンバー上の初期化子は、'Shared' メンバーおよび定数にのみ有効です。
+
  Public Data As Integer() = {0, 1, 2, 3, 4, 5}
+

          
+
End Structure
 
}}
}}
 

        

        
+
こういった構造体内に配列フィールドを作成する場合の注意点などについては[[programming/netfx/arrays/3_structfields]]で個別に詳しく解説しているのでそちらを参照してください。 また、構造体内に固定長の領域を作成する方法などについては[[programming/netfx/structlayout_fieldoffset]]を参照してください。
+

          
+

          
+
*バイト単位での操作 [#bytewise_operation]
+
VB.NETでは任意の構造体とバイト配列を相互に変換したり、構造体を直接読み書きするメソッドが用意されていません。 そのため、構造体の内容をファイルに保存したりバイト配列から構造体データを読み込んだりする場合には、独自にコードを記述する必要があります。
+

          
+
そのような処理を実現する方法については[[programming/netfx/tips/convert_struct_and_bytearray]]や[[programming/netfx/serialization/0_abstract]]などで解説しています。
+

          
+

          
+