(for Internet Explorer)
定数は、静的オブジェクトのメンバー変数に格納します。
定数シンボルが、グローバル・スコープのシンボルではなくなるので、名前の衝突の可能性が
低くなります。
Class  SampleClass
    Public  c
    Private Sub  Class_Initialize()
        Set Me.c = get_SampleConst()
    End Sub
End Class


'***********************************************************************
'* Function: get_SampleConst
'***********************************************************************
Dim  g_SampleConst

Function  get_SampleConst()
    If IsEmpty( g_SampleConst ) Then _
        Set g_SampleConst = new SampleConstClass
    Set get_SampleConst = g_SampleConst
End Function

Class  SampleConstClass
    Public  CutTag, ExistOnly

    Private Sub  Class_Initialize()
        CutTag = 1
        ExistOnly = 2
    End Sub

    Public Function  ToStr( Number )
        Select Case  Number
            Case CutTag : ToStr = "CutTag"
            Case ExistOnly : ToStr = "ExistOnly"
        End Select
    End Function
End Class
使用例1:
    Set c = get_SampleConstClass()

    copy_ex  src, dst, c.CutTag
c.CutTag
get_SampleConstClass
CutTag
CutTag
使用例2:
Sub  main()
    Set object = new ClassA
    copy_ex  source, destination, object.c.CutTag
End Sub
object.c.CutTag
get_SampleConst
参考
'***********************************************************************
'* Function: get_SampleConst
'***********************************************************************
Dim  g_SampleConst

Function  get_SampleConst()
    If IsEmpty( g_SampleConst ) Then _
        Set g_SampleConst = new SampleConstClass
    Set get_SampleConst = g_SampleConst
End Function

Class  SampleConstClass
    Public  CutTag, ExistOnly

    Private Sub  Class_Initialize()
        CutTag = 1
        ExistOnly = 2
    End Sub
End Class
get_SampleConst
キーワード:
グローバル変数
メンバー変数、文字列変換
ToStr
CutTag
CutTag
多くの場合、クラスに所属しない関数の中の最初で get_Object した後で、Validate メソッド
を呼び出します。 Validate は、パラメータに指定した状態にオブジェクトをするメソッドです。
Sub  FuncA()
  Dim  m : Set m = get_ClassA() : m.Validate  Empty
  m.MethodA
End Sub
演算器的なオブジェクトとは、インスタンスを特定する必要が無いオブジェクトのことで、
切り替えることはあっても同時に複数存在しなくても目的の出力が得られるものです。
ユーザーは演算器の種類 (ClassV) を指定します。
ClassV_validate で取得したオブジェクトを明示的に削除する必要はありません。
明示的に削除を指定したり、削除する許可をしたりする ClassI のメンバ関数を作っても
構いませんが、キャッシュのように、状況に応じて自動的に判断することが処理効率が
良いこともあります。
Dim  g_obj as ClassI
インターフェイスを
参照する変数
オブジェクトの
インターフェイス
オブジェクト(アトム)
get_Object 関数
オブジェクトを操作するインターフェイス(ClassI)は、演算器の種類(ClassV)と異なることが
あります。 さらに ClassV は、インスタンスのクラス(ClassA)と異なることもあります。
たとえば、ClassI =コンパイラ、ClassV = C言語コンパイラ、ClassA = A社製コンパイラver2。
状況や設定に応じて、適切な ClassA のインスタンスが返ります。 内部で、インスタンスが
生成されるかもしれないし、引数をそのまま維持するだけかもしれません。基本的に
インスタンスは1つ(シングレトン)ですが、マルチコアを生かしたり、マルチスレッドでレイテ
ンシを短くするために、Opt 引数に、新規インスタンスを指定することがあるかもしれません。
(VBScript は、基本的にシングルスレッドですが)
演算器的なオブジェクトのライフサイクル
参考
プログラムが使用する外部プログラムのバージョンが異なると、使用する
データが微妙に異なったりします。 たとえば、Visual Studio のプロジェクト・
ファイルを出力するプログラムは、Visual Studio のバージョンによって、
出力するファイルの拡張子が変わります。

プロジェクト・ファイルを出力する関数をプログラミングするときは、
バージョンを判定して、出力内容を変えるというのが一般的ですが、
規模が大きくなると、同じ条件分岐のコピーがたくさん記述されること
になり、メンテナンス性が低くなります。

たとえば、プロジェクト・ファイルの拡張子が変わると、ファイルを作成する
コードと、使用するプロジェクト・ファイルをソリューション・ファイル内に
記述するコードの2箇所に、バージョンを判定するコードが記述されます。
この状態で、将来、バージョンと拡張子の関係が変わったら、2箇所の修正が
必要なのに、1箇所だけ修正して完了としてしまうリスクがあるのです。
Dim  o : Set o = new MakeVS
o.SetMakeProjContext  MakeProjContext, get_MakeVSProjType( TypeName )
o.MakeProj

Class MakeVSProjType
 Public VSVer       '// as integer
 Public TargetPlatform  '// as string
 Public ProjectExtension '// as string
End Class

Function get_MakeVSProjType( TypeName )
 Dim ret, items
 Select Case TypeName        '// VSVer, TargetPlatform
  Case "VS2010Win32"  : items = Array( 2010, "Win32" )
  Case "VS2008Win32"  : items = Array( 2008, "Win32" )
  Case "VS2010Win32Lib" : items = Array( 2010, "Lib" )
  Case "VS2008Win32Lib" : items = Array( 2008, "Lib" )
  Case Else Error
 End Select

 Set ret = new MakeVSProjType
 ret.VSVer     = items( 0 )
 ret.TargetPlatform = items( 1 )
 If ret.VSVer = 2010 Then
  ret.ProjectExtension = ".vcxproj"
 Else
  ret.ProjectExtension = ".vcproj"
 End If
End Function
これを避けるには、プロジェクト・ファイルの拡張子を変数にすることです。
この状態で、バージョンと拡張子の関係が変わったら、変数の値を代入する
箇所だけを変えるだけで済みます。

このような違いを集めたオブジェクトは、Visual Studio のバージョンによって
異なるものであるため、バージョンごとに存在する静的オブジェクト(コンパ
イル時に値が決定する static な構造体)になります。

そして、静的オブジェクトを取得する関数を定義するとよいでしょう。 その
定義は、下記のようにテーブルで記述するとよいでしょう。 また、相関関係が
ある場合は、下記の ProjectExtension 変数のように条件分岐が使えます。
この条件分岐は、静的オブジェクトを取得する関数の中にまとめられるため、
メンテナンス性が高くなります。
 If VSVer <= 2008 Then  f.Open ".vcproj"  Else  f.Open ".vcxproj"
 f.Close
 f.Open  ".sln"
 If VSVer <= 2008 Then  f.Write ".vcproj"  Else  f.Write ".vcxproj"
    '// Derived property
    Private  m_DerivedAttribute

    Public  Property Get  DerivedAttribute()
        If IsEmpty( m_DerivedAttribute ) Then  m_DerivedAttribute = "Derived "+ Me.AnAttribute
        DerivedAttribute = m_DerivedAttribute
    End Property
read only attribute
DerivedAttribute
派生属性のキャッシュを持つときは、下記のようにコーディングします。
DerivedAttribute
DerivedAttribute
DerivedAttribute
DerivedAttribute
DerivedAttribute
m_ から始まるプライベート プロパティは、他の派生属性と並べると一覧性が高くてよいでしょう。
複数の派生属性を同時にキャッシュに載せいた方が、処理速度は速いでしょう。
参考
→ T_NameDelegator フォルダ
サンプルファイル
→ T_NameList フォルダ
→ T_Name フォルダ
Name と TrueName が、1対多の場合
Name と TrueName が、多対1の場合
Name と TrueName が、多対1の場合の一覧
Property Get  Name() as string
→ vbslib を使う vbsool サンプル
インスタンスの名前です。 クラス名と同じになることもあります。
Property Get  TrueName() as string
関数に指定する名前と同じです。
以下は、一般名 (Name) と正式名 (TrueName) の違いから生じる問題の対策法です。
→ Module Mixer 3 の実装
GetVar など)に正式名を指定することもできます。
クラス名やインスタンス名は、一般名と正式名があり、多くの場合、一般名を必要とします。しかし、
new に指定する型の名前や、TypeName で取得できる型の名前はクラスの正式名であることが
多く、扱いにくいでしょう。 そこで、
処理フロー
一般名は場面によって(抽象度が変化して)変わります。 正式名はインスタンスの内容や定義の
抽象度によって変わります。 一般名と正式名が、同じになることもあります。
一般名と正式名は、1対多の関係があるので、場面に応じて、一般名から正式名を選択する必要
があります。(同時に多対1の関係もありますが別の章を参照)。
( "Sample" )
new TrueSample
一般名 "Sample" のオブジェクトを取得する
'// Class  Sample  has_interface_of ClassI

Function  get_Sample()
  Set  get_Sample = get_Object( Sample_getTrueName() )
  get_Sample.Name = "Sample"
End Function

Function  Sample_getTrueName()
  Dim  ret : ret = GetVar( "Sample" )
  If IsEmpty( ret ) Then
    '// default is ClassA fixed
    ret = "TrueSample" : SetVar "Sample", ret
  End If
  Sample_getTrueName = ret
End Function

'// 言語上の Class Sample の定義は不要です。
一般名 "Sample" から正式名 "TrueSample" を選択
( "TrueSample" )
get_Object
上記は ret = "TrueSample" 固定ですが、場面に応じて TrueSample 以外の正式名を
返すように自動判定した方がよいです。
Name プロパティは、インスタンスの一般名とします。
TrueName プロパティは、インスタンスの正式名とします。
'// Class  TrueSample  has_interface_of ClassI

Dim  g_TrueSample

Function    get_TrueSample()  '// has_interface_of ClassI
  If IsEmpty( g_TrueSample ) Then _
    Set g_TrueSample = new TrueSample
  Set get_TrueSample =   g_TrueSample
End Function

Class  TrueSample
  Public  Name
  Public  Property Get  TrueName() : TrueName = TypeName(Me) : End Property
  '--- Name is factory pattern.
  Private Sub  Class_Initialize() : Name = TypeName(Me) : End Sub
    :
End Class
ClassI
ClassB
ClassI
ClassA
Name = "ClassN", TrueName = "ClassA"
Name = "ClassN", TrueName = "ClassB"
データ構造
→ T_Name フォルダ
サンプルファイル
一般名で選択
例: Windows
例: Windows XP, Windows7
正式名 "TrueSample" のオブジェクトを取得する
[Sample]
[Sample]
[TrueSample]
[TrueSample]
→ T_Name_vbslib フォルダ
→ T_NameAdd_vbslib フォルダ
多くの場合、自動判定を行いますが、環境変数(レジストリや、設定ファイルや、vbslib の
上記の正式名を決める処理は、Sample_getTrueName 関数(一般名 "Sample" の
getTrueName メソッド)ではなく、get_Sample 関数に書かれていることがあります。
Dim  g_NameDic : Set g_NameDic = CreateObject( "Scripting.Dictionary" )

Function  get_NameDelegator( Name, TrueName, InterfaceName )
  If g_NameDic.Exists( Name +"__"+ TrueName ) Then
    Set get_NameDelegator = g_NameDic.Item( Name +"__"+ TrueName +"_"+ InterfaceName )
    Exit Function
  End If

  Set  get_NameDelegator = new_X( InterfaceName + "_Delegator" ) : With get_NameDelegator
    .Name = Name
    .m_Delegate = TrueName  '// if validated was need.
    If not g_bNeedValidateDelegate Then _
      Set .m_Delegate = get_Object( TrueName )  '// if validated was not need.
  End With

  Set  g_NameDic.Item( Name +"__"+ TrueName +"_"+ InterfaceName ) = get_NameDelegator
End Function


Class NameDelegator  '// defined_as_interface
  Public  Name
  Public Property Get  TrueName() : TrueName = NameDelegator_getTrueName( Me ) : End Property
  Public  m_Delegate ' as ClassA or ClassB or string(before validated)
  Public Property Get  DefineInfo() : Set DefineInfo = m_Delegate.DefineInfo : End Property
End Class
複数から同じインスタンスを参照し、オブジェクトから一般名(Nameプロパティ)を取得する必要が
ある場合は、一般名と正式名が多対1の関係になり、次のような委譲をしないと、Name プロパティ
がそれぞれの場面に応じた一般名になりません。
'// Class  Sample  has_interface_of ClassI

Function    get_Sample()
  Set get_Sample = get_NameDelegator( "Sample", Sample_getTrueName(), "ClassI" )
End Function

Function  Sample_getTrueName()
  Dim  ret : ret = GetVar( "Sample" )
  If IsEmpty( ret ) Then
    '// default is ClassA fixed
    ret = "TrueSample" : SetVar "Sample", ret
  End If
  Sample_getTrueName = ret
End Function
'// Class  ClassA  has_interface_of ClassI

Dim  g_TrueSample

Function    get_TrueSample()
  If IsEmpty( g_TrueSample ) Then _
    Set g_TrueSample = new TrueSample
  Set get_TrueSample =   g_TrueSample
End Function


Class TrueSample
  Public  Property Get  Name()     : Name     = TypeName(Me) : End Property
  Public  Property Get  TrueName() : TrueName = TypeName(Me) : End Property
  '--- Name is factory pattern.

  Public  Value
  Public Function  Method1() : Method1 = "TrueSample.Method1" : End Function
End Class
Name プロパティは、ユーザーが取り扱っている場面(ドメイン)によって抽象度が変化して、同じ
インスタンスに対して複数の一般名を持つことになります。 このケースに対応する実装は委譲の
形態をとるとよいでしょう。

他のドメインの名前を参照する必要は、ほとんどありませんが、もし参照するのであれば、Names
という集合のプロパティになります。
場面Aの関数
オブジェクトの削除は、ガーベジコレクションに任せて、ClassI_Delegator が無くなるまで存在させ
るとよいでしょう。

実体のファイルが削除されたショートカットのように、ClassI_Delegator が参照する、正式名の
オブジェクトだけを無くすときは、一般名から委譲先を参照している ClassI_Delegator::m_Delegate
を Empty にしてガーベジコレクションを動かして削除します。 また、Empty にすれば、削除された
オブジェクトにアクセスしようとしたときに、きちんと例外が発生するようになります。
Name 以外は
参照
場面Aでの Name
場面Bでの Name
ある1つのインスタンス
TrueName
(正式名)
ClassI_Delegator クラス
(一般名)
処理フロー
( "Sample" )
一般名 "Sample" のオブジェクトを取得する
一般名 "Sample" から、正式名 "TrueSample" を選択
new ClassI_Delegator
new TrueSample
If not g_NameDic.Exists( Name +"__"+ TrueName +"_"+ InterfaceName ) Then
InterfaceName インターフェイスを持ち、
TrueName へ委譲するオブジェクトを生成
正式名のオブジェクトを生成する
サンプルファイル
→ T_NameDelegator フォルダ
[Sample]
[Sample]
[TrueSample]
場面Bの関数
データ構造
g_NameDic
Dictionary
.Item
.Key
string
= Name +"__"+ TrueName +"_"+ InterfaceName
.Name
.m_Delegate
string
= "Sample"
ClassI
参考
new_X
( InterfaceName + "_Delegator" )
( Name, TrueName, InterfaceName )
ClassI
.Name = Name
Set  .m_Delegate = get_Object( TrueName )
  Public Property Get  DefineInfo() : Set DefineInfo = m_Delegate.DefineInfo : End Property
DefineInfo プロパティは、オプションです。
Class ClassI_Delegator  '// has_interface_of NameDelegator and ClassI
  Public  Name
  Public Property Get  TrueName() : TrueName = NameDelegator_getTrueName( Me ) : End Property
  Public  m_Delegate ' as ClassA or ClassB or string(before validated)
  '--- Name is factory pattern.

  Public Property Let  Value( v ) : m_Delegate.Value = v : End Property
  Public Property Get  Value( )   : Value = m_Delegate.Value : End Property
  Public Function  Method1() : Method1 = m_Delegate.Method1() : End Function
End Class


Function  new_ClassI_Delegator()  '// has_interface_of NameDelegator and ClassI
  Set  new_ClassI_Delegator = new ClassI_Delegator
End Function
[ClassI_Delegator]
一般名を持ったオブジェクト
正式名を持ったオブジェクト
→ T_NameDelegator_vbslib フォルダ
例: Windows
例: Windows7
例: Windows7
オブジェクトの名前を一覧するときは、
取得して、Name プロパティを表示します。 TrueName は、必要なら補助的に表示します。
Sub   ClassI::Validate( Opt as variant )
ただし、すべてのオブジェクトを生成すると時間がかかる場合、生成時に実行する初期化処理
(Class_Initialize) を Validate メソッドに移すとよいでしょう。
で一覧するすべてのオブジェクトを
Dim  samples, sample
get_ObjectsFromFile  "Lib\*_obj.vbs", "ClassI", samples '// [out] samples
For Each sample  In samples : echo  sample.Name : Next
一覧するときは、一般名が Name プロパティに入っているオブジェクトと、正式名が Name
プロパティに入っているオブジェクトの両方を一覧します。
→ T_NameList フォルダ
→ T_NameList_vbslib フォルダ
サンプルファイル
Default Property  Item( Name as string ) as variant
VBScript は、ECMA Script (JavaScript) のように、動的にメンバーを定義することはできませんが、
デフォルト・プロパティと、辞書(Dictionary クラス)を使うことで、同様のことができます。
→ JavaScript のクラス
参考
Item プロパティは、Dictionary や FolderItem などに準拠しています。
ArrayClass(vbslib) も、Item プロパティが使えます。
→ T_Item フォルダ
サンプル:
  Dim obj : Set obj = new ClassA
  obj.PropA    = 1   '// Static  defined property
  obj("PropB") = 12  '// Dynamic defined property
  obj("PropC") = "ABC"
  Set obj("PropD") = new ClassA

  WScript.Echo  "PropA = "& obj.PropA
  WScript.Echo  "PropB = "& obj("PropB")
  WScript.Echo  "PropC = "& obj("PropC").Value
使用例:
Class ClassA
  Public  PropA  '// Static defined property

  '//=== Dynamic defined property : Item, Items, Keys
  Public Default Property Get  Item( Name ) : LetSet  Item, m_Dic( Name ) : End Property
  Public Property Let  Item( Name, Value ) : m_Dic( Name ) = Value : End Property
  Public Property Set  Item( Name, Object ) : Set m_Dic( Name ) = Object : End Property

  Public Property Get  Keys()  : Keys  = m_Dic.Keys  : End Property
  Public Property Get  Items() : Items = m_Dic.Items : End Property

  Public m_Dic  ' as Dictionary
  Private Sub  Class_Initialize()
    Set m_Dic = CreateObject( "Scripting.Dictionary" )
    m_Dic.CompareMode = 1  '// NotCaseSensitive
  End Sub

End Class

Sub  LetSet( Out, In_ )
  If IsObject( In_ ) Then  Set Out = In_ Else Out = In_
End Sub
vbslib なし版
関連
→ T_Item_vbslib フォルダ
vbslib 使用版
→ GetSetting 関数
Property Get  Items() as array of variant
  For Each item  In obj.Items
    If IsObject( item ) Then
      WScript.Echo  item.Value
    Else
      WScript.Echo  item
    End If
  Next
使用例:
サンプル
上記は標準形ですが、処理速度が要求されるときは次のようにしてください。
  For Each item  In obj.m_Dic.Items
Property Get  Keys() as array of string
  For Each name  In obj.Keys
    WScript.Echo  name
  Next
使用例:
サンプル
上記は標準形ですが、処理速度が要求されるときは次のようにしてください。
  For Each item  In obj.m_Dic.Keys
関連
関数定義のオーバーライド
Dim  ClassE::OnEventX as EventResponders
イベントとは、あるオブジェクトの状態変化のことです。
イベントが発生したときに行う処理は、アプリケーションによって異なります。
そこで、あるイベントが発生したときに行う処理を関数に記述して、その関数をオブジェクトに
登録しておくことで、必要なときに必要な処理ができるようになります。
他の言語では、関数ポインタ、コールバック関数、メソッドポインタなどと呼ばれています。
Class  ClassE
  Public  OnEventX  ' as EventResponders

  Private Sub  Class_Initialize()
    Set Me.OnEventX = new EventResponders
  End Sub
End Class
Sub  ClassA::OnEventX( Caller as ClassE, Args as variant )
Sub  main()
  Dim  obj : Set obj = new ClassE  '// defined by Library
  Dim  app : Set app = new ClassA  '// defined by Application

  app.RespondFromClassE  obj
  obj.Run  '// call ClassA::OnEventX
End Sub

Class  ClassA
  Public Sub  RespondFromClassE( Caller )
    Caller.OnEventX.Add  GetRef( "ClassA_onEventX" ), Me
  End Sub

  Public Sub  OnEventX( Caller, Args )
    WScipt.Echo  "Responded"
  End Sub
End Class

Sub  ClassA_onEventX( Me_, Caller, Args ) : Me_.OnEventX( Caller, Args ) : End Sub
Class ClassE
  Public  OnEventX  ' as EventResponders

  Public Sub  Run()
    Me.OnEventX.Calls  Me, Empty
  End Sub
End Class
OnEventX
app
ClassA_onEventX
obj
Responded
OnEventX
OnEventX
複数のメソッドが登録されているときは、Calls メソッドを1度呼び出すだけで、複数の
メソッドが呼び出されます。
→ イベント関数とパラメータ取得関数 (C言語)
OnEventX
参考
関数を複数回呼び出す
→ イベント(C#)
イベントが発生したオブジェクトのメソッドから、イベントに応答するオブジェクトのメソッドを
呼び出すコードを、通常、ライブラリが書きます。
イベントが発生したらコールバックされるメソッドを、イベントが発生するオブジェクトに登録
するコードを、通常、アプリケーションが書きます。
通常、ライブラリにあるクラスが、イベントの定義とメソッドの呼び出しを行い、アプリケーション
が、イベントに応答するメソッドの定義と、そのメソッドの登録を行います。
通常、ライブラリにあるクラスが、イベントを定義します。
ライブラリの中 (イベント発生元)
アプリケーション
呼び出し
アプリケーションが定義する ClassA クラスが応答する場合:
アプリケーションが定義する OnEventX 関数が応答する場合:
Sub  main()
  Dim  obj : Set obj = new ClassE

  obj.OnEventX.Add  GetRef( "OnEventX" ), Empty
  obj.Run  '// call OnEventX
End Sub

Sub  OnEventX( Dummy, Caller, Args )
  WScipt.Echo  "Responded"
End Sub
Responded
OnEventX
OnEventX
OnEventX
OnEventX
→ T_Item フォルダ
サンプル:
vbslib なし版
→ T_Item_vbslib フォルダ
vbslib 使用版