元ネタ
これに関して後輩と話していたところ「型推論効くっすよ、ディクショナリはいらねーしw」と言われ驚愕。提示されたのが下のソース。
[改訂版]基底クラス
AbstractSingletonClass.vb
''' <summary>
''' [改訂版]シングルトン基底クラス
''' </summary>
''' <remarks></remarks>
Public MustInherit Class AbstractSingletonClass(Of T)
''' <summary>
''' シングルトンインスタンス
''' ディクショナリ不要!!
''' </summary>
''' <remarks></remarks>
Private Shared _Instance As T = Nothing
''' <summary>
''' インスタンス生成排他ロックオブジェクト
''' </summary>
''' <remarks></remarks>
Private Shared ReadOnly _SyncLockCreateInstance As New Object
''' <summary>
''' コンストラクタ(Protected)
''' </summary>
''' <remarks></remarks>
Protected Sub New()
End Sub
''' <summary>
''' インスタンス取得
''' </summary>
''' <typeparam name="T"></typeparam>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetInstance() As T
SyncLock _SyncLockCreateInstance
'インスタンス生成済みかチェックする
If _Instance Is Nothing Then
'無ければ生成する
_Instance = CType(System.Activator.CreateInstance(GetType(T), True), T)
End If
'返す
Return CType(_Instance, T)
End SyncLock
End Function
End Class
派生クラス
SingletonA.vb
''' <summary>
''' 派生クラスA
''' </summary>
''' <remarks></remarks>
Public Class SingletonA
Inherits AbstractSingletonClass(Of SingletonA)
''' <summary>
''' コンストラクタ(Protected)
''' </summary>
''' <remarks></remarks>
Protected Sub New()
End Sub
End Class
使い方
'型推論が効く!!
Dim S = SingletonA.GetInstance()
え??これじゃ静的フィールドの_Instanceを入れ替えてるんだから2個目、3個目の派生クラスのインスタンスが保持れないでしょ?と指摘したのだが「いや、イケてますww」との返事。・・・確かに。
今回勉強になったポイント
ジェネリクスの静的フィールドの場合、型毎に保持される。
なので、ディクショナリを使わずとも1つの型に対して別々の静的フィールドが割り当てられるためインスタンスを正しく保持することができる。
残る問題
以前のクラスでは扱う型を基底クラスの派生に制限していたが、今回はクラス宣言の時点で型を決めるため制限することができない。
・・・が、そもそも制限しなければいけない理由が見つからなかった。結局、単に派生しただけではSingletonは保証されるわけではなく、派生先のコンスタラクタを隠すことを忘れてしまうと複数のインスタンスが生成可能になってしまう。(これを強制する仕組みは今のところ無い?)
最終的には
問題点よりもいちいちGetInstanceを書かなくてよい、インスタンス生成に型推論が効くということの恩恵の方がはるかに大きいので改訂版を使用することに決めた。