モジュールとはVB6以前のbasファイルのように、関数(もしくはプロシージャ・メソッド)や変数など一連のソースコードをひとかたまりにしておくためのものです。

VB6以前ではbasファイル自体が一つのモジュールとなっていましたが、 VB.NETでは一つのファイルに一つ以上のモジュールを記述することができます。 また、VB6以前にあった「basファイルはモジュール、clsファイルはクラス」といった拡張子による区別もなくなり、モジュールとクラスはどちらもvbファイルに記述でき、一つのファイルにモジュールやクラスを混在させたり複数記述させたりすることが出来るようになっています。 名称も、標準モジュールから単にモジュールと呼ばれるようになっています。

§1 モジュールの宣言

VB.NETでのモジュールは、VB6以前のようにファイル自体がモジュールとなるのではなく、Moduleキーワードを使って明示的に宣言する必要があります。

Module MainModule

    Sub Main()

        Console.WriteLine("Hello, world!")

    End Sub

End Module

これはもっとも基本的な「Hello, world!」プログラムですが、これがVB.NETプログラムの基本的な形でもあります。 ModuleキーワードからEnd Moduleキーワードまでがモジュールになり、同時に名前を与えることができます。 この場合のモジュールの名前は「MainModule」になっています。 モジュールは一つのvbファイルに複数記述できるので、次のようなコードも有効です。

' 一つ目のモジュール
Module MainModule

    .
    .

End Module

' 二つ目のモジュール
Module SubModule

    .
    .

End Module

モジュールという概念がわかりにくいのであれば、Module〜End Moduleを一つのbasファイルと見なして考えるのもいいかもしれません。

なお、一つのファイルでモジュール以外にも複数のクラス構造体を宣言することができます。

§2 クラスとモジュールの違い

VB.NETではクラスも一つのファイルではなくClassキーワードを使って宣言することができます。 クラスについてはクラスで詳しく説明しますが、モジュールとクラスの相違点を簡潔にまとめると次のようになります。

クラスとモジュールの相違点
機能 クラス モジュール
インスタンス化 できる できない
継承 できる できない
インターフェイスの実装 できる できない
メソッド Sharedを付ければ共有メソッドとなる すべてのメソッドは暗黙的に共有メソッドとなる
型名を省略したメンバの参照 できない できる

このように、モジュールは特殊なクラスととらえることができ、インスタンスを生成できないクラス・C#における静的クラスとほぼ同等のものとなります。

モジュールとクラスのどちらを使うべきかは状況によりますが、ユーティリティメソッドなど様々な場面で呼び出されるような共通処理などを記述する場合はモジュールが向いています。 一方、インスタンス化できるのはクラスとなるので、内部で状態を持つオブジェクトを複数表現するような場合はクラスが向いています。

§3 変数の宣言

モジュール内で変数を宣言するには、プロシージャ内などで変数を宣言するのと同様、Dimを使います。 変数の宣言と同時に、初期値を与えることもできます。 モジュール内で宣言した変数は、そのモジュールのどこからでも参照することが出来ます。

Module MainModule

    ' モジュール内変数の宣言と初期値の代入
    Dim message As String = "Hello, world!"

    Sub Main()

        Console.WriteLine(message)

        ' モジュール内変数の値を変更する
        SetMessage("こんにちわ世界")

        Console.WriteLine(message)

    End Sub

    ' モジュール内変数の値を設定するプロシージャ
    Sub SetMessage(ByVal newMessage As String)

      ' 引数で指定された値を設定する
      message = newMessage

    End Sub

End Module
実行結果
Hello, world!
こんにちわ世界

Dimで宣言した変数はモジュール内でのみ有効であり、他のモジュールやクラスからは参照することは出来ません。 一方、Dimの代わりにPublicで宣言すれば、他のモジュールやクラスからも参照することが出来るようになります。 他のモジュールで宣言されている変数には モジュール名.変数名 と記述することで参照できます。

Module MainModule

    Sub Main()

        ' モジュールSubModuleの変数Messageの内容を表示する
        Console.WriteLine(SubModule.Message)

        ' 他のモジュールの変数の値を変更する
        SubModule.Message = "こんにちわ世界"

        Console.WriteLine(SubModule.Message)

    End Sub

End Module

Module SubModule

    ' 他のモジュールからも参照できる変数
    Public Message As String = "Hello, world!"

End Module
実行結果
Hello, world!
こんにちわ世界

DimとPublicの違いやアクセス修飾子についてはアクセス修飾子を参照してください。

§4 モジュール名を省略したメンバの参照

モジュール同士が同一の名前空間にあるか、もしくはImportステートメントでモジュールの名前空間がインポートされている場合は、モジュールのメンバを参照する際にモジュール名を省略することが出来ます。 先にあげた例でも2つのモジュールは同一名前空間にあるためモジュール名を省略することができ、次のように記述することが出来ます。

モジュール名を省略したメンバの参照
Module MainModule

    Sub Main()

        ' モジュールSubModuleの変数Messageの内容を表示する
        Console.WriteLine(Message)

        ' 他のモジュールの変数の値を変更する
        Message = "こんにちわ世界"

        Console.WriteLine(Message)

    End Sub

End Module

Module SubModule

    ' 他のモジュールからも参照できる変数
    Public Message As String = "Hello, world!"

End Module
実行結果
Hello, world!
こんにちわ世界

vbNullやvbNewLineといったVB固有の定数群はConstantsモジュールで宣言されているものですが、モジュール名を省略して使用できるのはこの機能により実現されています。

§5 入れ子

モジュールを入れ子構造にすることも出来ます。 入れ子になったモジュール内のメンバには、外側のモジュール名.内側のモジュール名.メンバ名 と記述することでアクセスできます。

Module MainModule

    Sub Main()

        ' 入れ子になったモジュールの変数を参照する
        Console.WriteLine(SubModule.NestedModule.Message)

    End Sub

End Module

Module SubModule

    ' 入れ子になったモジュール
    Module NestedModule

        Public Message As String = "Hello, world!"

    End Module

End Module
実行結果
Hello, world!

なお、必要に応じてモジュールにもアクセス修飾子を付けることが出来ます。 モジュールのアクセス修飾子とメンバのアクセス修飾子の組み合わせによって、モジュール外部からのアクセスをどこまで許可するかを定めることが出来ます。

§6 Main()プロシージャ

Main()プロシージャは特殊なプロシージャで、プログラムの開始点となるプロシージャです。 Main()プロシージャは一つのプログラムに必ず一つ記述する必要があります。 全く記述しなかったり、二つ以上記述することはできません。 ただ、VB.NETではフォームをアプリケーションのエントリーポイントに指定した場合はMain()プロシージャが存在しない場合もあります。

Main()プロシージャはモジュールだけでなく、共有メソッドとしてクラスで宣言することも可能です。 Main()プロシージャの形式についてはアプリケーションの種類とコンソールアプリケーションを参照してください。

§7 New()プロシージャ

New()プロシージャも特殊なプロシージャで、これはVB.NETから導入されました。 すべてのモジュールはこのプロシージャを持つことができます。 New()プロシージャが記述されているモジュールでは、モジュールが読み込まれた時点でこのNew()プロシージャが実行されます。 次のコードでは、New()プロシージャでメッセージを初期化し、Main()プロシージャでそのメッセージを表示しています。

Module MainModule

    Dim message As String

    Sub New()

        message = "Hello, world!"

    End Sub

    Sub Main()

        Console.WriteLine(message)

    End Sub

End Module
実行結果
Hello, world!

New()プロシージャはクラスにおけるコンストラクタと同様の働きをします。