VBA技術解説
VBAクラスのAttributeについて(既定メンバーとFor Each)

ExcelマクロVBAの問題点と解決策、VBAの技術的解説
公開日:2019-10-19 最終更新日:2020-08-06

VBAクラスのAttributeについて(既定メンバーとFor Each)


VBAクラスをエクスポートすると各種のAttributeが設定されているのが確認できます。
それぞれのAttributeの意味と、さらに追加で指定できるAttributeについて説明します。


Attributeの変更はVBA標準でサポートされておらず、その使用については慎重であるべきですが、
どのようなものがあるかを知るのは、VBAの深部を知るうえでも役に立つと思います。

これらのAttributeは、基本的にはVBから移植されたものですが、
全てのAttributeが使えるわけではなく、VBAとしての制限があります。

※本記事を書くにあたっては以下のページを参考にしています。

このページは少々見づらく、また自動翻訳のため意味も分かりづらくなっています。
自身の覚え書きの意味も含めて、再検証しつつ制限等を追加解説したものになります。
基本的なVBAコードは、上記ページのコードを使っています。


VBAクラスのエクスポートとインポート

クラスのAttributeは、VBEでは追加・修正できません。
・クラスをエクスポート(.cls)
・(.cls)をテキストエディタで編集
・(.cls)をインポート
これにより指定したAttributeがVBAで有効となります。

クラスをエクスポートした(.cls)の内容は、

VERSION 1.0 CLASS
BEGIN
 MultiUse = -1 'True
END
Attribute VB_Name = "Class1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
・・・以下、通常のクラスコード・・・

上記のうち、以下はVBAでは指定が無効か変更しないものになります。
MultiUse = -1 ・・・ VBAでは無効
Attribute VB_Name = "Class1" ・・・ クラス名なので変更しない
Attribute VB_GlobalNameSpace = False ・・・ VBAでは無効
Attribute VB_Creatable = False ・・・ VBAでは無効

上記の中で指定が有効な、
Attribute VB_PredeclaredId
Attribute VB_Exposed
さらに、追加可能なAttributeとして、
Attribute procName.VB_Description
Attribute variableName.VB_VarUserMemId
Attribute procName.VB_UserMemId
以上について説明します。

Attribute VB_PredeclaredId

Attribute VB_PredeclaredId = True / False
既定はFalseとなっています。
Trueを指定することで、
クラスのグローバルデフォルトインスタンスが作成されます。
デフォルトのインスタンスは、クラスの名前を介してアクセスされます。
Newキーワードでインスタンスを作成することなく、直接クラス名が使えるようになります。

通常クラスを使用するときは、インスタンス変数を作成します。

Dim cls As New Class1
Debug.Print cls.item

Attribute VB_PredeclaredId = True
.clsにこのAttributeを指定しインポートしたクラスは、グローバルインスタンスが作成されます。
したがって、インスタンスを作成することなくクラスを使用できるようになります。

Debug.Print class.item

Attribute VB_Exposed

Attribute VB_Exposed = True / False
既定はFalseとなっています。
クラスのインスタンス化特性を制御します。
クラスのプロパティInstancingと連動しています。

VBA マクロ クラス Attribute

Private → Attribute VB_Exposed = False
PublicNotCreatable → Attribute VB_Exposed = True
ただし、VBAではVB_Createableが無視されるため、
クラスのインスタンスを外部から直接作成することはできません。

Attribute [procName.]VB_Description

Attribute VB_Description = "VBAクラスの説明"
オブジェクトエクスプローラで表示されるクラスにテキストの説明を追加します。

Attribute VB_Name = "Class1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Attribute VB_Description = "VBAクラスの説明"
※インポート前の.cls

VBA マクロ クラス Attribute

Attribute procName.VB_Description = "クラスの既定のメンバー"
オブジェクトエクスプローラで表示されるクラスのメンバーにテキストの説明を追加します。

Public Property Get item(ByVal index As Long) As Variant
  Attribute Item.VB_Description = "クラスの既定のメンバー"
  Attribute Item.VB_UserMemId = 0
  item = internal(index)
End Property
※インポート前の.cls

VBA マクロ クラス Attribute

Item.VB_Description
このItemは特にメンバー名と一致している必要はありませんが、通常は一致させておいたほうが良いでしょう。
Attribute Item.VB_Description
この挿入位置は、当該プロシージャー/プロパティの中(直後からEndの前)であればどこでも構いません。

注:
プロパティではすべてのアクセサメンバー(Get、Let、Set)は同じ説明を使用します。
したがって、どのアクセサに指定しても構いません。

Attribute variableName.VB_VarUserMemId

Attribute variableName.VB_VarUserMemId = 0
モジュールスコープ変数をクラスのデフォルトメンバーに指定します。
・変数宣言の直後に指定
・variableNameは変数名と一致させる
・モジュールレベルのPublic変数
以上の条件を満たしてください。
(この制限の一部は実際にやってみた上での制限で、ドキュメントに見当たらないものもあります。)

Public count As Long
Attribute count.VB_VarUserMemId = 0

Private Sub Class_Initialize()
  count = 123
End Sub
※インポート前の.cls

上記が書かれているクラスの場合、
標準モジュールではクラスのインスタンスだけ指定したときcountプロパティが既定となります。

Dim cls As New Class1
Debug.Print cls

clsだけの記述で、cls.countが既定メンバーとして動作します。
イミディエイトには「123」と出力されます。

Attribute procName.VB_UserMemId = 0

Attribute procName.VB_UserMemId = 0
クラスのデフォルトメンバーに指定します。
この挿入位置は、当該プロシージャー/プロパティの中(直後からEndの前)であればどこでも構いません。
procNameの文字列は特に使われていませんので、
プロシージャー/プロパティ名と一致している必要はありませんが、通常は一致させておいたほうが良いでしょう。

VERSION 1.0 CLASS
BEGIN
 MultiUse = -1 'True
END
Attribute VB_Name = "Class1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Attribute VB_Description = "VBAクラスの説明"
Option Explicit

Private internal As New Collection

Public Property Get item(ByVal index As Long) As Variant
  Attribute item.VB_Description = "クラスの既定のメンバー"
  Attribute item.VB_UserMemId = 0
  item = internal(index)
End Property

Public Property Set item(ByVal index As Long, ByVal value As Variant)
  With internal
    If index = .count + 1 Then
      .Add item:=value
    ElseIf index = .count Then
      .Remove index
      .Add item:=value
    ElseIf index < .count Then
      .Remove index
      .Add item:=value, before:=index
    End If
  End With
End Property
※インポート前の.cls

上記クラスの場合、
標準モジュールではクラスのインスタンス変数だけを指定したときItemプロパティが既定となります。

Sub test()
  Dim cls As New Class1
  Set cls(1) = Range("A1")
  Set cls(2) = Range("A2")
  Debug.Print cls(2).Address
End Sub

cls(n)は、cls.Item(n)になります。
イミディエイトには"$A$2"と出力されます。

Attribute procName.VB_UserMemId = -4

Attribute procName.VB_UserMemId = -4
For Eachループ構造でクラスを反復可能にします
このメンバーが列挙子を生成することをVBAに伝えます。

VERSION 1.0 CLASS
BEGIN
 MultiUse = -1 'True
END
Attribute VB_Name = "Class1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Attribute VB_Description = "VBAクラスの説明"
Option Explicit

Private internal As New Collection

Public Property Get item(ByVal index As Long) As Variant
  Attribute item.VB_Description = "クラスの既定のメンバー"
  Attribute item.VB_UserMemId = 0
  item = internal(index)
End Property

Public Property Let item(ByVal index As Long, ByVal value As Variant)
  With internal
    If index = .count + 1 Then
      .Add item:=value
    ElseIf index = .count Then
      .Remove index
      .Add item:=value
    ElseIf index < .count Then
      .Remove index
      .Add item:=value, before:=index
    End If
  End With
End Property

Public Property Get NewEnum() As IUnknown
  Attribute NewEnum.VB_Description = "リストを反復処理する列挙子を取得します。"
  Attribute NewEnum.VB_UserMemId = -4
  Set NewEnum = internal.[_NewEnum]
End Property
※インポート前の.cls

まず、既定のメンバーを用意します。
ここでは、Itemに、
Attribute item.VB_UserMemId = 0
これを指定しています。
そして、
Public Property Get NewEnum() As IUnknown
これを定義します。
Collectionの非表示メンバー[_NewEnum]を使っています。
_NewEnum_記号から始まるメンバーなので[ ]で囲みます。

Sub test()
  Dim cls As New Class1
  Dim rng As Range, ix As Long
  ix = 1
  For Each rng In Range("A1:B5")
    Set cls(ix) = rng
    ix = ix + 1
  Next
  For Each rng In cls
    Debug.Print rng.Address
  Next
End Sub

イミディエイトには、
$A$1
$B$1
$A$2
・・・
と出力されます。
ステップインで実行すると、For Eachの時点でNewEnumが動いているのが分かります。

※プロパティ名NewEnumは別の名称でも構いません、好きな名称にできます。

VBAクラスのAttributeの最後に

最初にも書きましたが、本記事は自身の覚え書きの意味も含めて書いたものになります。
実務において、このような技術を使う事はほとんどありませんが、
もし必要になったら、いつでも使えるように用意しておこうという事です。

そもそもこのAttributeは、VBから移植されたものですが、
VBの時から裏技的なものとなっていましたので、さすがにVBAで使う事に対する良し悪しはあるようにも思います。
ですが、実際にVBAではずっと使えているものでもあるので、今後もサポートされ続けるものと思われます。

通常、このような事をする必要性はないはずですが、
もしもの時には、ここを見ればいつでも実装できるように準備しておいたということです。



同じテーマ「VBAクラス入門」の記事

VBAクラスの作り方:列名のプロパティを自動作成する
VBAクラスの作り方:独自Rangeっぽいものを作ってみた
クラスとイベントとマルチプロセス並列処理
クラスとCallByNameとポリモーフィズム(多態性)
オートフィルターを退避回復するVBAクラス
オートフィルター退避回復クラスを複数シート対応させるVBAクラス
コレクション(Collection)の並べ替え(Sort)に対応するクラス
VBAクラスのAttributeについて(既定メンバーとFor Each)
VBAクラスを使ったイベント作成(Event,RaiseEvent,WithEvents)
VBAで音楽再生するクラスを作成
図形を方程式で動かすVBAクラス


新着記事NEW ・・・新着記事一覧を見る

ブール型(Boolean)のis変数・フラグについて|VBA技術解説(2024-04-05)
テキストの内容によって図形を削除する|VBA技術解説(2024-04-02)
ExcelマクロVBA入門目次|エクセルの神髄(2024-03-20)
VBA10大躓きポイント(初心者が躓きやすいポイント)|VBA技術解説(2024-03-05)
テンキーのスクリーンキーボード作成|ユーザーフォーム入門(2024-02-26)
無効な前方参照か、コンパイルされていない種類への参照です。|エクセル雑感(2024-02-17)
初級脱出10問パック|VBA練習問題(2024-01-24)
累計を求める数式あれこれ|エクセル関数応用(2024-01-22)
複数の文字列を検索して置換するSUBSTITUTE|エクセル入門(2024-01-03)
いくつかの数式の計算中にリソース不足になりました。|エクセル雑感(2023-12-28)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.RangeとCellsの使い方|VBA入門
4.ひらがな⇔カタカナの変換|エクセル基本操作
5.繰り返し処理(For Next)|VBA入門
6.変数宣言のDimとデータ型|VBA入門
7.ブックを閉じる・保存(Close,Save,SaveAs)|VBA入門
8.並べ替え(Sort)|VBA入門
9.セルのクリア(Clear,ClearContents)|VBA入門
10.Findメソッド(Find,FindNext,FindPrevious)|VBA入門




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。


このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
本文下部へ