以前以下の記事で書いたものは、デフォルトプロパティの設定だった。
thom.hateblo.jp
しかしその後、海外のサイトでさらにすごいテクニックを発見。
http://www.papwalker.com/ref101/ccol.html
かいつまんで説明する。
まずクラスモジュールを作成し任意のクラス名にしておく。
ここではMyCollectionというクラス名にした。
そして以下のコードを張り付ける。
※2015/10/03 「項目」メンバがSubになっている間違いがありましたのでFunctionに修正しました。
Dim 内部コレクション As Collection Private Sub Class_Initialize() Set 内部コレクション = New Collection End Sub Public Sub 追加(対象, Optional キー, Optional Before, Optional After) 内部コレクション.Add 対象, キー, Before, After End Sub Public Function 項目(Index) 項目 = 内部コレクション.Item(Index) End Function Public Function NewEnum() As IEnumVARIANT Set NewEnum = 内部コレクション.[_NewEnum] End Function
プロジェクトエクスプローラーからMyCollectionクラスを選択し、ファイルメニューからファイルのエクスポートをクリックしてデスクトップなどに保存する。
エクスポートしたら、同じくファイルメニューからMyCollectionクラスを解放しておく。(解放時にエクスポートするか聞かれるが、いいえを選択する。)
MyCollection.clsファイルをメモ帳などのテキストエディタで開くと、VBE上では見えないAttribute(属性)などが見える。
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END Attribute VB_Name = "MyCollection" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Dim 内部コレクション As Collection Private Sub Class_Initialize() Set 内部コレクション = New Collection End Sub Public Sub 追加(対象, Optional キー, Optional Before, Optional After) 内部コレクション.Add 対象, キー, Before, After End Sub Public Function 項目(Index) 項目 = 内部コレクション.Item(Index) End Function Public Function NewEnum() As IEnumVARIANT Set NewEnum = 内部コレクション.[_NewEnum] End Function
項目とNewEnumの下に、それぞれ以下のような属性を書き加える。
Public Function 項目(Index) Attribute Value.VB_UserMemId = 0 項目 = 内部コレクション.Item(Index) End Function Public Function NewEnum() As IEnumVARIANT Attribute NewEnum.VB_UserMemId = -4 Set NewEnum = 内部コレクション.[_NewEnum] End Function
上書き保存し、VBEのファイルメニューからこれをインポートする。
VBエディタ上からは書き加えたAttributeは見えない。
これでMyCollectionクラスの準備は完成。
後は標準モジュールに以下のようなコードを書いて実行してみると、
Sub test() Dim C As New MyCollection C.追加 "Hello" C.追加 "Good Bye" Debug.Print C(1) For Each x In C Debug.Print x Next End Sub
普通のコレクションと同じように使えていることが分かる。
このテクニックを使うと、コレクションの機能を自由に拡張できる。
例えば、中身を配列にして返すToArrayメソッド、内容をソートするSortメソッド、特定の型のみを許可するジェネリクスもどきなども実装できる。
難点はコレクションでは非表示メソッドであるNewEnumが見えてしまうこと。
非表示にするAttributeもありそうな気がするが、調査はまた今度にする。
For Each文はNewEnumの有無で繰り返し可能なオブジェクトかどうかを判定しているとのこと。
Collectionも非表示オブジェクトとして[_NewEnum]を持っているので、MyCollectionのNewEnumでは内部コレクションの[_NewEnum]を返している。
参照設定でOLE Automationが外れているとこのコードは動かない。
IEnumVARIANTを普通のVariantに書き換えればOLE Automationが参照されていなくても動作したので、そちらの方が確実かもしれない。
IEnumVARIANTはOLE Automationライブラリで定義されている非表示のインターフェースのようだ。
オブジェクトブラウザでstdoleを選択して非表示のメンバーを表示させ、検索すると出てくる。
ひょっとしてImplements IEnumVARIANTで各種メソッドを実装すればイテレータブルなオリジナルオブジェクトを作成できるかもしれないと思ったが、サポートされていないバリアント型を引数にとるメソッドがあるため、不正なインターフェースとされてしまうようだ。IUnknown型で同じことをしようと思ったが、こちらもうまくいかなかった。
何がしたかったかというと、コレクションの内部動作を人に説明するために、動的配列で疑似コレクションを作ろうと思ったのだ。
ただ、配列を返すプロパティを作らずに直接For Eachでオブジェクトを直接回せるようなものはうまく作れなかった。
なお、今回紹介したテクニックは以下の書籍(英語)でも紹介されている。
- 作者: Ken Getz,Mike Gilbert
- 出版社/メーカー: Sybex
- 発売日: 2001/04/25
- メディア: ペーパーバック
- クリック: 18回
- この商品を含むブログ (1件) を見る
以上