2014年4月8日

VB の Module ≒ C# の static class

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

0 件のコメント:

コメントを投稿