構造体

注意:
この文書は以前「.NETでいきまっしょい!」で公開していたものですが、公開以降メンテナンスされていません。 今や古い情報となった内容が記載されている場合があるのでご注意ください。

構造体とは

 VB6以前では構造体ではなくユーザー定義型という名称でした。 構造体とは基本データ型をいくつか組み合わせた構造を持った型のことです。 VB.NETでの構造体の仕様はVB6のユーザー定義型よりもC++の構造体に近くなっています。 また、宣言様式も変わっているのでまずはその構文を見てみましょう。
VB.NET での構造体の宣言
001
002
003
004
005
006
007
008
Structure Rect

    Dim Left As Integer
    Dim Top As Integer
    Dim Right As Integer
    Dim Bottom As Integer

End Structure
VB6以前 でのユーザー定義型の宣言
001
002
003
004
005
006
007
008
Type RECT

    Left As Long
    Top As Long
    Right As Long
    Bottom As Long

End Type
 この構造体は長方形座標を扱うRECT構造体のVB.NET版の一例です。 参考までにこれとほぼ等価なVB6でのコードを記述しておきました。 VB.NETではType〜End Typeではなく、Structure〜End Structureを用いて構造体を宣言します。 また、すべてのメンバ(フィールドとも言う)はDimを用いて宣言します。 これはメンバにアクセシビリティを設定できるようになったためです。(つまり、PublicやPrivateで宣言することもできます。 構造体のメンバは既定でPublicになります。) このように宣言した構造体を実際に使う場合の構文についてはVB6とかわりありません。
VB6以前 でのユーザー定義型の宣言
001
002
003
004
005
006
007
008
' 構造体型変数の宣言
Dim r As Rect

' 各メンバに値を設定する場合
r.Left = 0
r.Top = 0
r.Right = 320
r.Bottom = 240
 ちなみに、長方形の座標を扱う構造体はSystem.Drawing名前空間にRectangle型として既に.NET Frameworkに用意されています。 また、命名基準についてなのですが、VB.NETでは構造体でもすべて大文字からなる名前は付けないようにガイドラインで定められています。 ただ、あくまで指針であるので、わかりやすさが維持される限りは自由に名前を付けることができます。

メソッド

 VB.NETの構造体はC++並の仕様を持っているので、ほとんどクラスと同等の機能を持つことができます。 ただ、後で述べるつもりですがVB.NETにおけるクラスと構造体は明確な差があるので、使い分ける必要があります。 話を構造体の仕様に戻して、どんな機能が追加されたかというと、VB.NETの構造体ではメソッドを持つことができるようになりました。 ひとまず次のサンプルを見てください。 このサンプルでは先ほどのRect構造体に値を設定するためのメソッドを備えています。
VB6以前 でのユーザー定義型の宣言
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
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 MainModule

    Sub Main()

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

        ' 各メンバに値を設定する
        r.SetRect(0, 0, 320, 240)

    End Sub

End Module
 このように、VB.NETでは構造体にメソッドを持たせることができます。 Meキーワードは構造体自身がメンバを参照するためのキーワードで、たとえば引数のleftとメンバのLeftを区別するために用いています。 区別する必要がなければ次のようにMeキーワードを省略することもできます。
VB6以前 でのユーザー定義型の宣言
001
002
003
004
005
006
007
008
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
 また、値を返すFunctionメソッドを持たせることも可能です。
VB6以前 でのユーザー定義型の宣言
001
002
003
004
005
006
007
008
009
010
011
012
013
' 長方形の幅を返すメソッド
Function GetWidth() As Integer

    Return Right - Left

End Function

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

    Return Bottom - Top

End Function
 ただ、この場合はメソッドではなくプロパティにした方が設計的には優れているかもしれません。 プロパティについてはこの次で説明します。

プロパティ

 プロパティはすでにVBを使ったことがある方であれば概要は分かると思います。 ただ、VB6以前でのクラスのプロパティプロシージャの構文とは多少異なります。 プロパティ構文は次のようになっています。
VB6以前 でのユーザー定義型の宣言
001
002
003
004
005
006
007
008
009
Property プロパティ名() As 
    Get

    End Get

    Set(ByVal Value As )

    End Set
End Property
 VB6以前と異なり、Property Get、Property Letのように別々のプロシージャではなく、プロパティプロシージャの中にGetブロックおよびSetブロックが存在します。 また、SetとLetの区別は廃止され、すべてSetで扱います。 ちなみに、Visual Studio .NETではSetブロックのValueは自動的に付加されます。 この例は読み取りおよび書き込みが可能なプロパティですが、読み取り専用や書き込み専用にすることも可能です。 次のコードはその構文の例です。
読み取り専用プロパティ
001
002
003
004
005
ReadOnly Property プロパティ名() As 
    Get

    End Get
End Property
書き込み専用プロパティ
001
002
003
004
005
WriteOnly Property プロパティ名() As 
    Set(ByVal Value As Integer)

    End Set
End Property
 読み取り専用ではReadOnly、書き込み専用ではWriteOnlyキーワードを付加します。 さらに、一般的にプロパティ構文を使う場合には次の例のようにプロパティの値を保持するための外部に公開されない内部変数を用意しておきます。
001
002
003
004
005
006
007
008
009
010
011
Private m_Width As Integer

Property Width() As Integer
    Get
        Return m_Width
    End Get

    Set(ByVal Value As Integer)
        m_Width = Value
    End Set
End Property
 先ほどのRect構造体のGetWidth()およびGetHeight()メソッドを読み取り専用プロパティとして書き換えると次のようになります。
001
002
003
004
005
006
007
008
009
010
011
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
 これらのプロパティが実際に動作するかを次のコードで試してみます。
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
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

    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

End Structure

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
出力結果
Width: 480, Height: 360

コンストラクタ

 構造体にNewという名前のメソッドを作成すると、それはコンストラクタメソッドとして扱われます。 コンストラクタは構造体変数が宣言されたときに呼び出される特殊なメソッドで、主にメンバ変数の初期化を行います。 ただし、構造体のコンストラクタは必ず引数を取る必要があります。 引数のないコンストラクタは暗黙的に作成されています。 次のコードは先ほどのRect構造体に、宣言時に初期化できるようなコンストラクタを追加した例です。
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
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)

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

    End Sub

    .
    .
    以下同じ
    .
    .

End Structure
 そして、変数宣言時にこのコンストラクタを使用する場合は次のようにNewキーワードを使用します。
001
002
003
004
005
006
' コンストラクタを使用した例
Dim r As New Rect(160, 120, 640, 480)

' 次のコードは上のコードと等価です
Dim r As Rect
r.SetRect(160, 120, 640, 480)