自作クラスのオブジェクト型デフォルトメンバ……???
自作クラスのオブジェクト型デフォルトメンバ……???
ちょっと衝撃的な実行結果が出たので報告。
自作クラスにデフォルトメンバを設定する
これは、『VBA Developer's Handbook Second Edition』に載っていたテクニック。
VBA界隈では有名な id:t-hom さんもブログで紹介していたりする。
これをちょっとやってみたのである。
やり方は、ちょっと面倒だけれど簡単。
- クラスモジュールを一旦エクスポートする
- テキストエディタで開く
- デフォルトメンバにしたいプロシージャを選ぶ
- 当該プロシージャの先頭に、
Attribute XXXX.VB_UserMemId = 0
(XXXX
は、当該プロシージャの識別子)を追加して保存 - プロジェクトに戻って、インポートし直す
これでオッケー。
たとえば、PoweredSheet
というクラスモジュールを次のように作成したとする。
リスト1 クラスモジュール PoweredSheet
Option Explicit Private self_ As Worksheet Public Function Self() As Worksheet Set Self = self_ End Function Public Sub init(ByVal tgtSheet As Worksheet) Set self_ = tgtSheet End Sub
モジュールレベル変数self_
にWorksheet
オブジェクトを持たせておいて、Self
メソッドで返す、というだけのもの。
こいつをエクスポートして、テキストエディタで次のように編集する。
リスト2 エクスポートしたPoweredSheet.cls
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True' END Attribute VB_Name = "PoweredSheet" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit Private self_ As Worksheet Public Function Self() As Worksheet Attribute Self.VB_UserMemId = 0 '……(*)' Set Self = self_ End Function Public Sub init(ByVal tgtSheet As Worksheet) Set self_ = tgtSheet End Sub
付け加えたのは(*)の1行のみ。
こいつを上書き保存して、もとのプロジェクトにインポートし直す。
オブジェクト ブラウザー を開いてみてみると、
Self
がデフォルトメンバになっていることがわかる。
衝撃の実行結果
問題はここから。
Self
がデフォルトメンバだとは言っても、たとえば、
Private Sub test00() Dim ps As PoweredSheet Set ps = New PoweredSheet Call ps.init(Sh01) Debug.Print ps.Name End Sub
とすれば、ps
だけでps.Self
のように振る舞ってくれるというわけではない。
そもそもコード入力時に
このようになる。デフォルトメンバであるSelf
(Worksheet
型)のメンバが自動表示されるわけでもない。
強引にたとえば「ps.Name
」と入力したとて、
こうなってしまう。そもそもコンパイルが通らないという屈辱の結果。
ならば、と、
Private Sub test00() Dim ps As PoweredSheet Set ps = New PoweredSheet Call ps.init(Sh01) Dim sh As Worksheet Set sh = ps Debug.Print sh.Name End Sub
としたとしても、
実行時エラーになる。なんたる屈辱……!
秘策、発動す
そこでふと、「これ、カッコで括ったらどうなるんやろ???」と思い、やってみた。
リスト3
Private Sub test00() Dim ps As PoweredSheet Set ps = New PoweredSheet Call ps.init(Sh01) Dim sh As Worksheet Set sh = (ps) '……(*)' Debug.Print sh.Name End Sub
変えたのは(*)のところだけ。PoweredSheet
型の変数ps
をカッコで括ってみた。
「カッコで括っていっぺん評価させてみたらいいんでね?」と思いついたのだ。
リスト3を実行してみると……
何ごともなく完走した上、ちゃんとSheet1
とイミディエイト ウインドウに出力されている。
まるでPoweredSheet
クラスのインスタンスをWorksheet
型変数にほぼそのまま突っ込んだみたいになった。
おわりに
ただし、だからといって
(ps).Name
としてもダメです。
このように入力しても、行を移動した途端、
こうなりますw
自作クラスにオブジェクト型のデフォルトメンバを設定することは、半分可能、ということでいいのでしょうか。
私としては世紀の発見のつもりなのですが、「そんなもん常識じゃボケ!」なんでしょうか……???