Java/C# ユーザーが初めて VB.NET に触れた時、「Module(モジュール)って何?」という疑問にぶち当たる確率は高いと思う。
簡単に言ってしまえば、静的メンバのみのクラス(C# の static class)で、呼び出し側でクラス名(モジュール名)を省略できる特徴がある。
文字だと分かりにくいのでコードで
Public Module ModuleSample
Sub New()
' コンストラクタも定義できる(静的コンストラクタに該当)
Console.WriteLine("ModuleSample.New")
End Sub
Public Sub TestCall()
' Shared が無いけど、静的メソッド(共有メソッド)
Console.WriteLine("ModuleSample.TestCall")
End Sub
End Module
Module EntryPoint
' モジュール/クラスの Main メソッドはプログラムのエントリポイント
Sub Main()
' 呼び出し時
TestCall() 'モジュール名を省略できる
ModuleSample.TestCall() '省略しないことも可能
End Sub
End Module
'[結果]
' ModuleSample.New
' ModuleSample.TestCall
' ModuleSample.TestCall
C# からは、static class として扱える。
static class EntryPoint
{
static void Main()
{
ModuleSample.TestCall();
}
}
//[結果]
// ModuleSample.New
// ModuleSample.TestCall
Module の実態は static class とほぼ同じなのだが、C# の static class を VB から呼ぶ場合は Module のようにクラス名を省略できない。その違いは、
StandardModuleAttribute である。
この属性を C# の static class に付けてやると、VB から呼ぶ場合にクラス名を省略できる。
ただ、MSDN の説明に「独自に作成したコードから直接使用するためのものではありません。」「これは、コードから直接呼び出すためのものではありません。」とあり、ユーザーコードからは使って欲しくない模様・・・
using System;
using Microsoft.VisualBasic.CompilerServices;
[StandardModule]
public static class CShapModuleSample
{
public static void TestCall()
{
Console.WriteLine("CShapModuleSample.TestCall");
}
}
MSIL の比較
VB - Module |
.class public auto ansi sealed |
C# - static class |
.class public abstract auto ansi sealed beforefieldinit |
static class には、abstract/beforefieldinit が付いている。
static class の場合、beforefieldinit は静的コンストラクタがあると付かないが、Module の場合、コンストラクタの有無に関わらず付かない。
つまり、Module のフィールドは必ず最初のアクセス時に初期化される。(アクセスがなければ初期化されない)
拡張メソッド
VB の拡張メソッドは Module でしか作れない。
Imports System.Runtime.CompilerServices
Public Module Extensions
<Extension()>
Public Function IsNotNull(Of T)(ByVal source As T) As Boolean
Return source IsNot Nothing
End Function
End Module
' 呼び出し方法は 3 通り可能・・・
Dim val = ""
val.IsNotNull()
IsNotNull(val)
Extensions.IsNotNull(val)
まとめ
Module は多用しない方がいい。
クラス名(モジュール名)を省略することは、名前の衝突確率を上げることになり、あまりよくない。
プログラムのエントリポイント、拡張メソッド以外での利用は避けた方がいい。
どうしても使いたい場合は、アクセス修飾子を Friend にする、専用の名前空間にする、などの条件を設けるべき。
余談だが、拡張メソッドも名前の衝突確率が高いので、専用の名前空間を推奨。
VB は Module があるためか、静的クラスが無いので、代わりに下記のような疑似静的クラスを使ったりする。
' NotInheritable で継承禁止
' Private コンストラクタでインスタンス生成を禁止
Public NotInheritable Class StaticClass
Private Sub New()
End Sub
' ---- 以降、Shared メンバを定義 ----
End Class
検証環境
Windows 7 64bit/Visual Studio 2010 SP1/.NET 4.0