クラスとは、簡単に言うと構造体のようにいくつかのデータを持ち、それらのデータに対して処理を行う手続き(メソッドもしくはプロシージャ)も合わせ持つ複合データ型です。 ここで言うクラス(Class)は「学級・教室」などの意味ではなく、「種類・分類・階級」の意味の方を表します。

§1 VB.NETにおけるクラス

VB.NETにおけるクラスは多くの部分で構造体と類似していますが、両者の違いをまとめると次のようになります。

  • クラスは参照型であり、構造体は値型である (値型と参照型)
  • クラスはファイナライザ(デストラクタ)を実装できるが、構造体はできない (オブジェクトの破棄)
  • クラスは継承を行うことができるが、構造体はできない

このような特徴はVB.NETに固有のものではなく、C#など他の.NET Frameworkの言語とも共通するものとなっています。



§2 クラスの宣言

一つのclsファイルに一つのクラスを記述していたVB6以前とは異なり、VB.NETではClassキーワードを使ってクラス宣言します。 宣言の例は次の通りです。

' Accountクラスの宣言
Class Account
  ' IDを格納するフィールド
  Public ID As Integer
  ' 名前を格納するフィールド
  Public Name As String
End Class

クラスの宣言はClassキーワードを用いて宣言し、End Classまでがクラス宣言になります。 その間でフィールド(メンバ変数)、プロパティ、メソッドなどを宣言します。 フィールドなどクラスのメンバにはPublicなどのアクセシビリティを指定することができます。

また、構造体モジュールと同様に、クラスも一つのファイルに任意個のクラスを宣言することができます。 クラスと構造体を一つのファイルでまとめて宣言することもできます。

sample.vb
' クラスC1の宣言
Class C1
End Class

' クラスC2の宣言
Class C2
End Class

' 構造体S1の宣言
Structure S1
End Structure

§3 インスタンス

構造体とは異なり、クラスはクラス型変数を宣言しただけでは使用できません。 インスタンスを作成して宣言した変数に代入することで初めてクラスを使えるようになります。

Classキーワードによって宣言したクラスはただの定義であり、それを実体化したものがインスタンスであり、インスタンスを作成することを「クラスをインスタンス化する」といいます。 クラスとは設計図であり、インスタンスとはその設計図を元に作られた個々の製品であるとイメージすればわかりやすいかもしれません。

クラスとクラス型変数、インスタンスの関係を次のコードを使って説明します。

Imports System

' クラス(宣言)
Class Account
  Public ID As Integer
  Public Name As String
End Class

Module Sample
  Sub Main()
    Dim alice As Account ' クラス型変数

    alice.ID = 1
    alice.Name = "Alice"
  End Sub
End Module

このコードの概要を説明すると、まずIDとNameという二つのフィールドを持ったAccountクラスが宣言されています。 そして、このクラス型の変数aliceMainプロシージャで宣言しています。 さらに、宣言した変数aliceに対して、IDフィールドとNameフィールドにそれぞれ値を割り当てています。

このコードは一見動作するように見えるかもしれませんが、実際にはIDフィールドに値を設定しようとした時点で次のような例外エラーが発生します。

例外エラー
ハンドルされていない例外 : System.NullReferenceException: オブジェクト参照がオブジェクト インスタンスに設定されていません。
   at Sample.Main() in E:\sample.vb:line 12

このエラーメッセージは、変数aliceにはインスタンスへの参照が設定されていないために、IDフィールドを設定する対象のインスタンスが無いことが原因で発生しています。 このように、具体的なインスタンスを格納していない場合クラス変数には初期値としてNothingが代入されているため、そのような変数を使ってインスタンスに対して操作を行おうとすると例外NullReferenceExceptionがスローされます。

クラスはインスタンス化することで使用できるようになります。 インスタンスの作成にはNewキーワードを使用します。 先ほどの例とは異なり、次のコードは問題なく動作します。

Imports System

Class Account
  Public ID As Integer
  Public Name As String
End Class

Module Sample
  Sub Main()
    Dim alice As Account

    ' 新たにAccountクラスのインスタンスを作成し、変数aliceへ代入
    alice = New Account()

    ' 作成したインスタンスに対して操作を行う
    alice.ID = 1
    alice.Name = "Alice"
  End Sub
End Module

次のように、変数の宣言およびインスタンスの作成と変数への代入を一行で記述することもできます。

' 変数aliceを宣言し、Accountクラスのインスタンスを作成して代入する
Dim alice As Account = New Account()

alice.ID = 1
alice.Name = "Alice"

これをさらに簡略化した次のような構文もサポートされています。 どちらも同じ結果となりますが、一般的にはこの形式が多く使われているようです。

' 変数aliceを宣言し、Accountクラスのインスタンスを作成して代入する
Dim alice As New Account()

alice.ID = 1
alice.Name = "Alice"

インスタンスのフィールドに値が代入されているかを確認すると次のようになります。

Imports System

Class Account
  Public ID As Integer
  Public Name As String
End Class

Module Sample
  Sub Main()
    ' 変数aliceを宣言し、Accountクラスのインスタンスを作成して代入する
    Dim alice As New Account()

    alice.ID = 1
    alice.Name = "Alice"

    Console.WriteLine("ID = {0}, Name = {1}", alice.ID, alice.Name)
  End Sub
End Module
実行結果
ID = 1, Name = Alice

§4 構造体との共通点

クラスと構造体は機能的にはさまざまな点が共通しています。 メソッド、プロパティ、コンストラクタなどの要素は構造体の場合とほとんど変わりません。 クラスでは構造体と異なり引数を取らないコンストラクタを作成できるようになっています。

Imports System

Class Account
  ' 内部的にIDを保持するためのフィールド
  Private _ID As Integer
  ' 内部的に名前を保持するためのフィールド
  Private _Name As String

  ' Newという名前のメソッドはコンストラクタとして扱われる
  Public Sub New(ByVal id As Integer, ByVal name As String)
    _ID = id
    _Name = name
  End Sub

  ' クラスでは引数を取らないコンストラクタを作成することができる
  Public Sub New()
    _ID = 0
    _Name = "(default)"
  End Sub

  ' 次のような宣言により、クラスにプロパティを持たせることもできる
  Public Property ID() As Integer
    Get
      ' プロパティを介して返される値を返す
      Return _ID
    End Get
    Set(ByVal Value As Integer)
      ' プロパティを介して設定される値を保持する
      _ID = Value
    End Set
  End Property

  Public Property Name() As String
    Get
      Return _Name
    End Get
    Set(ByVal Value As String)
      _Name = Value
    End Set
  End Property

  ' メソッドを持たせることができるほか、基底クラスのメソッドを
  ' オーバーライドして動作を変えることもできる
  Public Overrides Function ToString() As String
    Return "ID = " + _ID.ToString() + ", Name = " + _Name
  End Function
End Class

Module Sample
  Sub Main()
    ' コンストラクタにパラメータを指定しないでインスタンスを作成する
    Dim alice As New Account()

    ' プロパティの値を変更する
    alice.ID = 1
    alice.Name = "Alice"

    ' コンストラクタにパラメータを指定してインスタンスを作成する
    Dim bob As New Account(2, "Bob")

    ' コンストラクタにパラメータを指定してインスタンスを作成する
    Dim charlie As New Account(3, "Charlie")

    ' プロパティの値を変更する
    charlie.ID = 999

    ' ToStringメソッドを呼び出して文字列化して表示する
    Console.WriteLine(alice.ToString())
    Console.WriteLine(bob.ToString())

    ' Console.WriteLineは自動的にToStringを呼び出して文字列化することもできるので
    ' このようにしても同じ
    Console.WriteLine(charlie)
  End Sub
End Module
実行結果
ID = 1, Name = Alice
ID = 2, Name = Bob
ID = 999, Name = Charlie

メソッド、プロパティ、コンストラクタの宣言方法については構造体での例モジュールプロシージャプロパティを参照してください。 またフィールドやメソッドのスコープ(アクセシビリティ)についてはアクセス修飾子で解説しています。

§5 参照型と値型

クラスと構造体の違いの一つに、値型であるか参照型であるかという違いがあります。 クラスは参照型で、構造体は値型です。

値型の変数は常にインスタンスを持ち、その値に対して直接アクセスするのに対して、参照型の変数ではインスタンスへの参照を通してインスタンスにアクセスします。 また、値型の変数に対して代入を行うとその値がコピーされるのに対して、参照型の変数に対して代入を行うと参照のみが代入され、実際のインスタンスはコピーされません。

この違いを明確にするコードを使って解説します。

Imports System

' クラス(参照型)で実装した型
Class RefAccount
  Public Name As String
End Class

Module Sample
  Sub Main()
    ' クラス(参照型)のインスタンスを作成
    Dim a1 As New RefAccount
    Dim a2 As New RefAccount

    a1.Name = "Alice"
    a2.Name = "Bob"

    ' それぞれのNameフィールドの値を表示
    Console.WriteLine("{0} {1}", a1.Name, a2.Name)

    ' a1が参照しているインスタンスへの参照がa2に代入される
    a2 = a1

    ' a2.Nameの値を変更
    a2.Name = "Charlie"

    ' それぞれのNameフィールドの値を再度表示
    Console.WriteLine("{0} {1}", a1.Name, a2.Name)
  End Sub
End Module
実行結果
Alice Bob
Charlie Charlie
Imports System

' 構造体(値型)で実装した型
Structure ValAccount
  Public Name As String
End Structure

Module Sample
  Sub Main()
    ' 構造体(値型)のインスタンスを作成
    Dim a1 As New ValAccount
    Dim a2 As New ValAccount

    a1.Name = "Alice"
    a2.Name = "Bob"

    ' それぞれのNameフィールドの値を表示
    Console.WriteLine("{0} {1}", a1.Name, a2.Name)

    ' a1のコピーがa2に代入される
    a2 = a1

    ' a2.Nameの値を変更
    a2.Name = "Charlie"

    ' それぞれのNameフィールドの値を再度表示
    Console.WriteLine("{0} {1}", a1.Name, a2.Name)
  End Sub
End Module
実行結果
Alice Bob
Alice Charlie

この二つのコードにはクラスを使っているか構造体を使っているかの違いしかありませんが、実行結果の違い、参照型では代入後のa2に対する変更がa1にも影響している点に注目してください。

構造体(値型)では、代入によって代入元変数a1のコピーが作成され代入先a2に設定されるため、代入先と代入元はどちらも違うインスタンスとなります。 一方クラス(参照型)では、代入によって代入元変数a1が参照しているインスタンスへの参照が代入先a2に設定されるため、代入先と代入元はどちらも同じインスタンスを参照することになります。 このため、クラスの場合では代入によってa1a2には同じインスタンスへの参照が設定されるため、a2に対する変更はa1に対して変更することと同じとなり、実行結果にあるようにどちらも同じ値が表示されることになります。

このように、クラスと構造体はどちらも同じような機能を持っていますが、一方で値型と参照型という明確な動作の違いもあります。 どちらを選ぶかによってパフォーマンスが大きくことなることもあるほか、List(Of T)で構造体のフィールド・プロパティを変更する場合のようにがわかりづらい問題が発生することもあるので、安易な判断を行わず両者の違いをよく理解して使い分ける必要があります。

値型・参照型の挙動の違いや注意点については値型と参照型、インスタンスのコピーについてはオブジェクトの複製で詳しく解説しています。

§6 共有メンバ

クラスのフィールド・プロパティ・メソッドなどすべてのメンバはインスタンスを作成することで初めて使用できるようになります。 一方、Sharedキーワードによってメンバを共有メンバにすると、全インスタンスに共通したメンバ(各インスタンスではなく直接クラスに属するメンバ)となり、インスタンスを作成せず使用できるようになります。 クラス外から共有メンバを参照する場合は、クラス名.メンバ名のように記述します。

Imports System

Class Account
  ' 共有メンバ
  Private Shared Count As Integer = 0

  ' 非共有(インスタンス)メンバ
  Public ID As Integer
  Public Name As String

  Public Sub New()
    ' インスタンスが作成される度にCountをカウントアップする
    Count += 1
  End Sub

  ' 共有メソッド
  Public Shared Sub PrintCount()
    Console.WriteLine(Count)
  End Sub
End Class

Module Sample
  Sub Main()
    ' インスタンスを3つ作成する
    Dim alice As New Account()
    Dim bob As New Account()
    Dim charlie As New Account()

    ' インスタンスメンバの変更
    alice.ID = 1
    alice.Name = "Alice"

    ' 共有メソッドを呼び出して作成されたインスタンスの数を表示する
    Account.PrintCount()
  End Sub
End Module
実行結果
3

アプリケーションのエントリポイントであるMainメソッドをクラス内で宣言する場合もSharedで宣言します。 エントリポイントが呼び出される前にインスタンスを作成することはできないため、インスタンスを作成せずにMainメソッドを呼び出せるようSharedを付ける必要があります。

Imports System

Class Sample
  ' アプリケーションのエントリポイント
  Shared Sub Main()
    Console.WriteLine("Hello, world!")
  End Sub
End Class

インスタンスを作成せずにメソッド呼び出しや変数の参照ができるモジュールは、すべてのメンバがShared(共有メンバ)となったクラスと見ることもできます。 一方、クラス自体にSharedキーワードを設定することはできないため、VBのクラスではC#の静的クラスに相当するものを作成することはできません。 VBではモジュールを使うことにより静的クラスに相当する機能を得ることができます。

§7 インスタンスの破棄

Newによって作成したインスタンスはガベージコレクタによって管理され、不要と判断された時点で自動的に破棄されるため、基本的にインスタンス自体を明示的に破棄する必要はなく、また解放処理を記述する必要もありません。

Imports System

Class Account
  Public Name As String
End Class

Module Sample
  Sub Main()
    ' コンストラクタにパラメータを指定しないでインスタンスを作成する
    Dim alice As New Account()

    ' インスタンスを使った処理
    alice.Name = "Alice"
    '    :
    '    :
    '    :

    ' インスタンスが不要になっても明示的に破棄する必要はない
  End Sub
End Module

しかし、インスタンスがアンマネージリソースなどガベージコレクタによる管理の対象外となるリソースを作成・使用している場合は、それを確実に解放する必要があります。 ガベージコレクタの管理対象外となるリソースを扱うクラスを作成する場合は、IDisposableインターフェイスを実装して明示的にリソースを解放できる手段を用意するしておくことが推奨されます。 また、そのようなクラスのインスタンスを使用する際においてはUsingステートメントを使うようにし、インスタンスを使用しおわった時点で確実に解放処理を呼び出させるようにします。

Imports System

' 明示的な解放が必要になるリソースを扱うクラス
Class Resource
  ' IDisposableインターフェイスを実装する
  Implements IDisposable

  Public Sub Dispose() Implements IDisposable.Dispose
    ' リソースの解放処理を記述する
  End Sub
End Class

Module Sample
  Sub Main()

    ' Usingステートメント内でインスタンスを作成・使用することにより、
    ' Disposeメソッドによる解放処理が確実に実行されるようにする
    Using res As New Resource()
      ' リソースを使った処理
    End Using

  End Sub
End Module

次のようにFinalizeメソッドをオーバーライドすることによりデストラクタを記述することもできますが、このメソッドを明示的に呼び出すことはできません。 デストラクタはガベージコレクタによって呼び出されます。 IDisposableを実装するクラスでは、確実に1度はDisposeメソッドが呼び出されるようにデストラクタを記述することはありますが、それ以外の多くの場合ではデストラクタを実装する必要はありません。

Imports System

Class Resource
  Protected Overrides Sub Finalize()
    ' Finalizeメソッドをオーバーライドしてデストラクタを記述する場合は、
    ' 基底クラスのFinalizeメソッドも呼び出す必要がある
    MyBase.Finalize()
  End Sub
End Class

Module Sample
  Sub Main()
    Dim res As New Resource()

    ' このようにして直接デストラクタを呼び出すことはできない
    res.Finalize()
  End Sub
End Module

インターフェイスの実装については後述します。

IDisposableインターフェイスとリソースの解放、デストラクタの適切な実装についてはオブジェクトの破棄で詳しく解説しています。

§8 インスタンスの保存・復元

.NET Frameworkでは、シリアライズによってインスタンスの状態を保存・復元することができます。 シリアライズを行うことにより、インスタンスをバイナリ形式やXML形式でファイルに保存したり、ネットワークを経由して送受信したりすることができます。 シリアライズによるインスタンスの保存・復元では、クラスごとに保存・読み込み処理を記述する必要はなく、また属性を付与することによって保存・復元時の動作を簡単にカスタマイズすることもできます。

シリアライズについてはシリアライズの基本で詳しく解説しています。

§9 継承

VBではクラスの継承を行うことができます。 継承はクラスのみでサポートされる機能で、構造体は継承を行うことはできません。 また、VBでは多重継承を行うことはできません。

§9.1 クラスの継承

クラスを継承して派生クラスを作成するには、Inheritsキーワードを使用します。 VBでは多重継承はサポートされないため、Inheritsの後ろには常に基底クラスを一つだけ指定します。

Imports System

' 基底クラス
Class Base
  Sub Print()
    Console.WriteLine("Base")
  End Sub
End Class

' 派生クラス
Class Derived
  Inherits Base ' Baseクラスを継承
End Class

Module Sample
  Sub Main()
    Dim d As New Derived()

    d.Print()
  End Sub
End Module
実行結果
Base

Inheritsによって別のクラスを継承しているクラスを基底クラスとして更に継承を重ねることもできます。

§9.2 オーバーライド

派生クラスで基底クラスのメソッドをオーバーライドする場合は、Overridesキーワードを使用します。 ただし、オーバーライドする場合はオーバーライドを許可することをあらわすOverridableキーワードが基底クラスのメソッドに付与されている必要があります。 Overridableキーワードが付与されているメソッドは仮想メソッドとなります。 Overridesキーワードが付与されているメソッドは暗黙的にOverridableとなるため、Overrides Overridableのようにこれらのキーワードを記述する必要はありません。

Imports System

' 基底クラス
Class Base
  ' オーバーライド可能なメソッド
  Overridable Sub Print()
    Console.WriteLine("Base")
  End Sub
End Class

' 派生クラス
Class Derived
  Inherits Base

  ' 基底クラスのPrintメソッドをオーバーライドして動作を変える
  Overrides Sub Print()
    Console.WriteLine("Derived")
  End Sub
End Class

Module Sample
  Sub Main()
    Dim d As New Derived()

    d.Print()
  End Sub
End Module
実行結果
Derived

§9.3 抽象メソッド・抽象クラス

メソッドを抽象メソッドとする場合は、Overridableの代わりにMustOverrideキーワードを指定します。 抽象メソッドを宣言する場合はメソッドの実装とEnd Subの記述を省略します。 またMustOverrideが付与されたメンバを含むクラスでは、そのクラスが抽象クラスであることを表すMustInheritキーワードをクラス宣言に付与する必要があります。 抽象クラスの継承と抽象メソッドのオーバーライドは通常のクラス・仮想メソッドの場合と同様です。

Imports System

' 抽象クラス
MustInherit Class Base
  ' 抽象メソッド
  MustOverride Sub Print()
End Class

' 派生クラス
Class Derived
  Inherits Base

  ' 抽象メソッドPrintの実装を記述する
  Overrides Sub Print()
    Console.WriteLine("Derived")
  End Sub
End Class

Module Sample
  Sub Main()
    Dim d As New Derived()

    d.Print()
  End Sub
End Module
実行結果
Derived

ここまでの例では値を返さないメソッド(Subプロシージャ)を使ってきましたが、値を返すメソッド(Functionプロシージャ)やプロパティ(Property)の場合も同様にキーワードを指定することでメンバの仮想化・抽象化・オーバーライドを行うことができます。

OverridesなどのキーワードとともにPublicProtectedなどのキーワードを組み合わせて指定することにより、メンバのアクセシビリティ(公開範囲)も設定することもできます。 これらのキーワードについては詳しくはアクセス修飾子で解説しています。

§9.4 継承の禁止

クラスにNotInheritableキーワードを付与することにより、継承を禁止してシール(sealed, 封印)されたクラスを作成することができます。 NotInheritableが付与されているクラスを継承しようとした場合はコンパイル時にエラーとなります。

Imports System

' 継承を許可しないクラス
NotInheritable Class Base
  Sub Print()
    Console.WriteLine("Base")
  End Sub
End Class

' 派生クラス
Class Derived
  Inherits Base ' BC30299: 'Derived' をクラス 'Base' から継承できません。'Base' は 'NotInheritable' として宣言されています。
End Class

§9.5 基底クラスのメンバ参照

派生クラス側から基底クラスを参照するにはMyBaseキーワードを使います。 例えば、派生クラスで基底クラスのコンストラクタを呼び出す場合は次の例のようにMyBase.New(引数リスト...)とします。 コンストラクタで基底クラスのコンストラクタを呼び出す場合は、コンストラクタでの他の処理よりも先に呼び出すようにする必要があります。

Imports System

' 基底クラス
Class Base
  Private _name As String

  Sub New(ByVal name As String)
    _name = name
  End Sub

  Sub Print()
    Console.WriteLine(_name)
  End Sub
End Class

' 派生クラス
Class Derived
  Inherits Base

  Sub New(ByVal name As String)
    ' コンストラクタに渡された値を基底クラスのコンストラクタに引き渡す
    MyBase.New(name)
  End Sub
End Class

Module Sample
  Sub Main()
    Dim d As New Derived("Derived")

    d.Print()
  End Sub
End Module
実行結果
Derived

コンストラクタだけでなく、メソッド・フィールド・プロパティなどの場合も同様にMyBaseで基底クラスのメンバを参照することができます。 また、基底クラスを参照するMyBase以外にも、インスタンス自身を表すMeキーワード、自クラスのメンバを参照するMyClassキーワードなどを使ってメンバの参照を行うことができます。

Me, MyClass, MyBaseの3つのキーワードについてはMe, MyClass, MyBaseで詳しく解説しています。

§9.6 実行時の型判別

実行時にオブジェクトがあるクラスを継承しているか(型に互換性があるか)どうかを調べるにはTypeOf ... Isを使用することができます。

Imports System

' 基底クラス
Class Base
End Class

' Baseを継承した派生クラス
Class Derived
  Inherits Base
End Class

' Baseとは無関係のクラス
Class Other
End Class

Module Sample
  Sub Main()
    Dim d As New Derived()
    Dim o As New Other()

    If TypeOf d Is Base Then
      Console.WriteLine("dはBaseクラスと互換性のある型です")
    Else
      Console.WriteLine("dはBaseクラスと互換性のない型です")
    End If

    If TypeOf o Is Base Then
      Console.WriteLine("oはBaseクラスと互換性のある型です")
    Else
      Console.WriteLine("oはBaseクラスと互換性のない型です")
    End If
  End Sub
End Module
実行結果
dはBaseクラスと互換性のある型です
oはBaseクラスと互換性のない型です

TypeOf演算子・Is演算子についてはIs演算子・IsNot演算子・TypeOf演算子および型・インスタンスが派生クラスかどうかを調べるで詳しく解説しています。

§10 インターフェイスの実装

VBではクラスにインターフェイスを実装することができます。 クラスの多重継承が許可されない代わりにインターフェイスは複数実装することができるため、インターフェイスを適切に実装することによってクラスに様々な機能を持たせることができます。

インターフェイスを実装するには、Implementsキーワードを使用してクラスの先頭で実装するインターフェイス名を指定します。 次に、インターフェイスメソッドの実装を記述します。 この際もImplementsキーワードを使用し、メソッド宣言の後ろにImplements インターフェイス名.メソッド名といった記述を付記します。 このような実装をインターフェイスが持つメソッドの分だけ記述します。

以下の例はクラスにIDisposableインターフェイスを実装する例です。 IDisposableインターフェイスは引数なし・戻り値無しのメソッドDisposeを持っているので、これをクラス内で実装するようにします。

Imports System

Class Resource
  ' IDisposableインターフェイスを実装する
  Implements IDisposable

  ' IDisposableインターフェイスのメソッドDisposeを実装する
  Sub Dispose() Implements IDisposable.Dispose
    ' メソッド内の実装は省略
  End Sub
End Class

クラスは複数のインターフェイスを実装することができます。 次の例では、二つのインターフェイスI1とI2を実装するクラスを作成しています。 次の例のように、実装しようとするインターフェイスに同名のメソッドが含まれている場合は、実装となるメソッド名を変えることにより同名のインターフェイスメソッドを別々の名前で実装することができます。

Imports System

Interface I1
  Sub Method()
End Interface

Interface I2
  Sub Method()
End Interface

Class C
  ' 二つのインターフェイスI1, I2を実装する
  Implements I1, I2

  ' I1インターフェイスのメソッドMethodを、M1という名前のメソッドとして実装する
  Sub M1() Implements I1.Method
  End Sub

  ' I2インターフェイスのメソッドMethodを、M2という名前のメソッドとして実装する
  Sub M2() Implements I2.Method
  End Sub
End Class

.NET Frameworkには様々な役割を持つインターフェイスが用意されています。 代表的なものとして、オブジェクトの破棄を行う機能を提供するIDisposableインターフェイス(オブジェクトの破棄)、コレクションにFor Eachによる列挙機能を持たせるIEnumerableインターフェイス(IEnumerable・IEnumerator)、オブジェクト同士の大小関係を比較する機能を提供するIComparableインターフェイス(大小関係の定義と比較)、オブジェクトをシリアライズする際の動作を制御するISerializableインターフェイス(BinaryFormatter・SoapFormatter §.シリアライズ動作の制御 (ISerializable))などがあります。 インターフェイスの具体的な実装例については、個々のドキュメントを参照してください。

§11 入れ子

クラスでは別のクラスを入れ子にして宣言することができます。

Imports System

' 型のコンテナとなるクラス
Class C
  ' 入れ子になったクラス
  Class NC
  End Class

  Sub M()
    ' 入れ子になったクラスのインスタンスを作成・使用する
    Dim nc As New NC()
  End Sub
End Class

入れ子にしたクラスにはアクセシビリティを設定できるため、クラス内でしか入れ子クラスを使用しない場合には入れ子クラスをPrivateにするといったこともできます。 このようにクラスは型のコンテナとして機能するため、クラスだけでなく構造体列挙体モジュールデリゲート・インターフェイスなどもクラス内に宣言することができます。

§12 宣言の分割 (Partial)

VB2005以降では、Partialキーワードを使用することによりクラスおよび構造体の宣言を分割することができます。 クラスのコードが長大になる場合や、自動生成部分と手動記述部分を分けて管理したい場合などには、Partialキーワードを使ってクラス宣言を複数箇所に分割して全体を把握しやすくすることができます。 Partialキーワードを使ったクラスの宣言は、個別のファイルに記述することも、同一のファイルに記述することもできます。

source1.vb
Imports System

' フィールド宣言のみを含むクラス宣言
Partial Class Account
  Public ID As Integer
  Public Name As String
End Class
source2.vb
Imports System

' コンストラクタとメソッドの宣言を含むクラス宣言
Partial Class Account
  Public Sub New(ByVal id As Integer, ByVal name As String)
    Me.ID = id
    Me.Name = name
  End Sub

  Public Overrides Function ToString() As String
    Return "ID = " + ID.ToString() + ", Name = " + Name
  End Function
End Class

Module Sample
  Sub Main()
    Dim alice As New Account(1, "Alice")

    Console.WriteLine(alice)
  End Sub
End Module
実行結果
ID = 1, Name = Alice

§13 クラスライブラリ

プロジェクトを実行可能形式(.exe)ではなくライブラリ(.dll)としてビルドすることにより、クラスライブラリを作成することができます。 これにより、作成したクラスを別のプロジェクトでも使用できるようにすることができます。 また、このようにして作成したクラスライブラリはプロジェクトの参照に追加するだけで使用できるようになり、またVBだけでなくC#など.NET Frameworkの他の言語からも使用することができるようになっています。

クラスライブラリの作成方法などについてはクラスライブラリの作成で詳しく解説しています。