[VBA]自作クラスに説明を追加する

オブジェクトブラウザを見ていると、クラスによっては以下のような説明がついていることがある（「指定した～」の箇所）。
![dic1.png](https://qiita-image-store.s3.amazonaws.com/0/142842/6d4d58a9-7029-41e2-5267-f7b96af068ba.png)

この説明を自作クラスに追加する方法について自身の覚え書きとしてまとめる。

# はじめに

この投稿の内容は
>[VB Attributes: What are they and why should we use them?](
https://christopherjmcclellan.wordpress.com/2015/04/21/vb-attributes-what-are-they-and-why-should-we-use-them/ "VB Attributes: What are they and why should we use them?")（英語記事）

の記事の内容を自身の覚え書き用にまとめたものとなる。

# Attributeの設定

上記の説明などを追加するためには、該当する変数・プロシージャにAttributeを設定する必要がある。
このAttributeはVBE上では設定しにくいため、一度エクスポートを行いテキストエディタで設定する。

以降はエクスポートした後の状態を前提に進める。


## プロシージャに設定する

以下の記述をプロシージャ内に追加する。

```vb
Attribute プロシージャ名.VB_Description = "プロシージャの説明"
```

例えば、`Item`というプロパティプロシージャに説明を追加する場合は以下のようになる。

```vb
Public Property Get Item(
'...

Attribute Item.VB_Description = "指定したキーに対する項目を設定します。値の取得も可能です。"

'...
End Property
```

プロシージャ内に記述されていれば、`プロシージャ名`は適当でも問題無さそうだが、変数の場合と統一するためプロシージャ名を指定した方が無難である。

## 変数に設定する

以下の記述を変数宣言の直後に追加する。

```vb
Attribute 変数名.VB_VarDescription = "変数の説明"
```

構文自体はほぼ同じで、`VB_Description`から`VB_VarDescription`に変更されたのみとなる。

例えば、`TestVar `という変数に説明を追加する場合は以下のようになる。

```vb
Public TestVar As Variant
Attribute TestVar.VB_VarDescription = "テスト用変数"
```

変数の場合、変数名をちゃんと指定しないと認識しないため注意が必要となる。

## その他属性

Description以外の属性としては、UserMemIdが存在する。
こちらは既定のプロパティの設定や、ForEachのイテレータの指定に使われる。

記述する場所はDescriptionの場合と同じで、0を指定すると既定メンバーとして扱われる（クラス内に一つのみであること）。

```vb
Attribute プロシージャ名.VB_UserMemId = 0
Attribute 変数名.VB_VarUserMemId = 0
```

## サンプル

Scripting.Dictionaryをラップし、ほぼ同じ説明を追加したクラス。

以下のコードをコピーし、**新規テキストファイルにペーストして、文字コードはShiftJIS、「任意の名前.cls」で保存する。**
保存したファイルを適当なプロジェクトにインポートしオブジェクトブラウザを確認すると、以下のように表示される。
![dic2.png](https://qiita-image-store.s3.amazonaws.com/0/142842/07abd42e-3df4-772f-17a7-ab1a56272379.png)


```vb
VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "DictionaryWrapper"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

Private clsDic As Object    'As Scripting.Dictionary
Attribute clsDic.VB_VarDescription = "ラップされたディクショナリです。"

Private Sub Class_Initialize()
    Set clsDic = VBA.CreateObject("Scripting.Dictionary")
End Sub

Private Sub Class_Terminate()
    Call clsDic.RemoveAll
    Set clsDic = Nothing
End Sub


'ディクショナリに新しいキーおよび項目を追加します。
Public Sub Add(Key As Variant, Item As Variant)
Attribute Add.VB_Description = "ディクショナリに新しいキーおよび項目を追加します。"
    Call clsDic.Add(Key, Item)
End Sub


'文字列を比較するメソッドを設定します。値の取得も可能です。
Public Property Get CompareMode() As VbCompareMethod
Attribute CompareMode.VB_Description = "文字列を比較するメソッドを設定します。値の取得も可能です。"
    Let CompareMode = clsDic.CompareMode
End Property

Public Property Let CompareMode(Mode As VbCompareMethod)
    Let clsDic.CompareMode = Mode
End Property


'ディクショナリ内の項目数を取得します。
Public Property Get Count() As Long
Attribute Count.VB_Description = "ディクショナリ内の項目数を取得します。"
    Let Count = clsDic.Count
End Property


'指定したキーがディクショナリに含まれているかどうかを示します。
Public Function Exists(Key As Variant) As Boolean
Attribute Exists.VB_Description = "指定したキーがディクショナリに含まれているかどうかを示します。"
    Let Exists = clsDic.Exists(Key)
End Function


'指定したキーに対する項目を設定します。値の取得も可能です。
Public Property Get Item(Key As Variant) As Variant
Attribute Item.VB_Description = "指定したキーに対する項目を設定します。値の取得も可能です。"
Attribute Item.VB_UserMemId = 0
    Call assignValue(clsDic.Item(Key), Item)
End Property

Public Property Let Item(Key As Variant, Value As Variant)
    Let clsDic.Item(Key) = Value
End Property

Public Property Set Item(Key As Variant, Value As Variant)
    Set clsDic.Item(Key) = Value
End Property


'ディクショナリ内のすべての項目を含む配列を取得します。
Public Function Items() As Variant
Attribute Items.VB_Description = "ディクショナリ内のすべての項目を含む配列を取得します。"
    Let Items = clsDic.Items
End Function


'キーを別のキーに変更します（設定のみ）。
Public Property Let Key(OldKey As Variant, NewKey As Variant)
Attribute Key.VB_Description = "キーを別のキーに変更します（設定のみ）。"
    Let clsDic.Key(OldKey) = NewKey
End Property


'ディクショナリ内のすべてのキーを含む配列を取得します。
Public Function Keys() As Variant
Attribute Keys.VB_Description = "ディクショナリ内のすべてのキーを含む配列を取得します。"
    Let Keys = clsDic.Keys
End Function


'指定したキーをディクショナリから削除します。
Public Sub Remove(Key)
Attribute Remove.VB_Description = "指定したキーをディクショナリから削除します。"
    Call clsDic.Remove(Key)
End Sub


'ディクショナリからすべての情報を削除します。
Public Sub RemoveAll()
Attribute RemoveAll.VB_Description = "ディクショナリからすべての情報を削除します。"
    Call clsDic.RemoveAll
End Sub


Private Sub assignValue(ByRef InputVal As Variant, ByRef OutputVal As Variant)
    If VBA.IsObject(InputVal) Then
        Set OutputVal = InputVal
    Else
        Let OutputVal = InputVal
    End If
End Sub
```

# 説明をつけてどこで役に立つの？

……正直微妙。
ソースを確認できる状況ならソースを見る人が多いと思われる。
わざわざオブジェクトブラウザを使う人がどれほど居るのか？

インテリセンスで出てくるのなら話は別だが、VBEにはそこまでの機能は無い。

パスワードを設定したプロジェクトでも、オブジェクトブラウザでプロシージャなどは見ることができるため、そういった場面なら役に立つかもしれません。

# 参考

>[VB Attributes: What are they and why should we use them?](
https://christopherjmcclellan.wordpress.com/2015/04/21/vb-attributes-what-are-they-and-why-should-we-use-them/ "VB Attributes: What are they and why should we use them?")

>[VBAの自作クラスでデフォルトプロパティを設定する方法](http://thom.hateblo.jp/entry/2015/02/16/003000 "VBAの自作クラスでデフォルトプロパティを設定する方法")

>[格納順を保持するディクショナリクラスを作ってみた](http://d.hatena.ne.jp/miau/20110203/1296706824 "格納順を保持するディクショナリクラスを作ってみた")

>[VBAでクラスを定義する](http://qiita.com/Kamo123/items/a4c7749fa30d8f68df28 "VBAでクラスを定義する")