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

ExcelマクロVBAの問題点と解決策、エクセルVBAの技術的解説
最終更新日:2019-10-20

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のクラスとは(Class,Property,Get,Let,Set)
クラスを使って他ブックのイベントを補足する
VBAクラスの作り方:列名の入力支援と列移動対応
VBAクラスの作り方:列名のプロパティを自動作成する
VBAクラスの作り方:独自Rangeっぽいものを作ってみた
クラスとイベントとマルチプロセス並列処理
オートフィルタを退避回復するVBAクラス
オートフィルタ退避回復クラスを複数シート対応させるVBAクラス
コレクション(Collection)の並べ替え(Sort)に対応するクラス
VBAクラスのAttributeについて(既定メンバーとFor Each)
VBAクラスを使ったイベント作成(Event,RaiseEvent,WithEvents)


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

VBAにおける変数のメモリアドレスについて|VBA技術解説(11月8日)
空文字列の扱い方と処理速度について(""とvbNullString)|VBA技術解説(1月7日)
Errオブジェクトとユーザー定義エラー|VBA入門(11月5日)
シングルクォートの削除とコピー(PrefixCharacter)|VBA技術解説(11月4日)
ユーザー定義型の制限とクラスとの使い分け|VBA技術解説(11月3日)
クリップボードに2次元配列を作成してシートに貼り付ける|VBA技術解説(11月1日)
VBAクラスを使ったイベント作成(Event,RaiseEvent,WithEvents)|VBA技術解説(10月31日)
VBAクラスのAttributeについて(既定メンバーとFor Each)|VBA技術解説(10月19日)
VBAの用語について:ステートメントとは|VBA技術解説(10月16日)
VBAのマルチステートメント(複数のステートメントを同じ行に)|VBA技術解説(10月14日)


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

1.最終行の取得(End,Rows.Count)|VBA入門
2.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.RangeとCellsの使い方|ExcelマクロVBA入門
4.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
5.変数宣言のDimとデータ型|ExcelマクロVBA入門
6.繰り返し処理(For Next)|ExcelマクロVBA入門
7.マクロって何?VBAって何?|ExcelマクロVBA入門
8.ひらがな⇔カタカナの変換|エクセル基本操作
9.空白セルを正しく判定する方法(IsEmpty,IsError,HasFormula)|VBA技術解説
10.セルに文字を入れるとは(Range,Value)|VBA入門



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

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


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




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