自作クラスのオブジェクト型デフォルトメンバ……???

自作クラスのオブジェクト型デフォルトメンバ……???

ちょっと衝撃的な実行結果が出たので報告。

自作クラスにデフォルトメンバを設定する

これは、『VBA Developer's Handbook Second Edition』に載っていたテクニック。

VBA界隈では有名な id:t-hom さんもブログで紹介していたりする。

thom.hateblo.jp

これをちょっとやってみたのである。

やり方は、ちょっと面倒だけれど簡単。

  • クラスモジュールを一旦エクスポートする
  • テキストエディタで開く
  • デフォルトメンバにしたいプロシージャを選ぶ
  • 当該プロシージャの先頭に、Attribute XXXX.VB_UserMemId = 0XXXXは、当該プロシージャの識別子)を追加して保存
  • プロジェクトに戻って、インポートし直す

これでオッケー。

たとえば、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行のみ。

こいつを上書き保存して、もとのプロジェクトにインポートし直す。

オブジェクト ブラウザー を開いてみてみると、

f:id:akashi_keirin:20200214074202j:plain

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のように振る舞ってくれるというわけではない。

そもそもコード入力時に

f:id:akashi_keirin:20200214074205j:plain

このようになる。デフォルトメンバであるSelfWorksheet型)のメンバが自動表示されるわけでもない。

強引にたとえば「ps.Name」と入力したとて、

f:id:akashi_keirin:20200214074208j:plain

こうなってしまう。そもそもコンパイルが通らないという屈辱の結果。

ならば、と、

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

としたとしても、

f:id:akashi_keirin:20200214074211j:plain

実行時エラーになる。なんたる屈辱……!

秘策、発動す

そこでふと、「これ、カッコで括ったらどうなるんやろ???」と思い、やってみた。

リスト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を実行してみると……

f:id:akashi_keirin:20200214074215j:plain

何ごともなく完走した上、ちゃんとSheet1とイミディエイト ウインドウに出力されている。

まるでPoweredSheetクラスのインスタンスWorksheet型変数にほぼそのまま突っ込んだみたいになった。

おわりに

ただし、だからといって

(ps).Name

としてもダメです。

f:id:akashi_keirin:20200214074219j:plain

このように入力しても、行を移動した途端、

f:id:akashi_keirin:20200214074222j:plain

こうなりますw

自作クラスにオブジェクト型のデフォルトメンバを設定することは、半分可能、ということでいいのでしょうか。

私としては世紀の発見のつもりなのですが、「そんなもん常識じゃボケ!」なんでしょうか……???