#主な話
- VBAでクラスに分類されるもの
- PredeclaredIdって何してるの
- ThisWorkbookとSheet
経験則で書いてるので間違っていることあれば指摘お願いします。
##VBAでクラスに分類されるもの
最初の記事でちょろっと触れたが、ユーザーフォームはクラス。
他にもThisWorkbookやSheetもクラスだがこいつらはフォームより特殊でNewできなかったり、既定のインスタンスにNothingを設定できなかったりする。
エクスポートして中身をテキストエディタで見るそれぞれこうなる。
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "ThisWorkbook"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Option Explicit
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "Sheet1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Option Explicit
VERSION 5.00
Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} UserForm1
Caption = "UserForm1"
ClientHeight = 3015
ClientLeft = 120
ClientTop = 465
ClientWidth = 4560
OleObjectBlob = "UserForm1.frx":0000
StartUpPosition = 1 'オーナー フォームの中央
End
Attribute VB_Name = "UserForm1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
ユーザーフォームはVERSION 5.00となっていてCLASSと書いていないがNewできる。
どれもPredeclaredIdがTrueになっている。
これは普通のクラスを取り出して設定したことがある人もいると思うが、Trueにするとクラス名が既定のインスタンスとして設定され、Class1.Methodのような形でどこからでもメソッドを呼び出せるようになるというもの。
後述するがこれ目的なら別にファイルに取り出して書き換えて入れ直すなんて面倒なことはしなくてもいいというのが正直なところ。
なおclsファイルになってしまって戻せないThisWorkbookとSheetは別として、frmファイルのこれをFalseにしてもインポートすると勝手にTrueにされるので、既定のインスタンスのないフォームというものは作れない。
##Attribute VB_PredeclaredId = Trueは何をしているのか
'普通のクラスの使い方
Dim ClassDim As Class1
Set ClassDim = New Class1
Call ClassDim.Method
'PredeclaredId = Trueにしたクラスの使い方
Call Class1.Method
このようにインスタンスを作らないでもクラス名からメソッドを呼べるというのが特徴。
なのだが、実際のところやってるのは標準モジュールで以下のように宣言してるのと大差ない。
Option Explicit
Public Class1 As New Class1
なぜかと言うとPredeclaredId = Trueに設定したクラスは最初に呼び出されたときにInitializeが働く。
つまり最初に参照されたときにNewされているので自動的にクラス名と同名の変数がAs Newで宣言されるような設定ということ。
厳密にはなにか違うのかもしれないが、わざわざ取り出して設定して入れ直して……というのが手間だと思うならこれも手だと思う。
結局Nothingと比較しても常にFalseになることや、意図しないタイミングでInitializeが走るという点では変わらないので……。
###ユーザーフォームで確認してみる
Dim A As UserForm1
Dim B As UserForm1
Set A = UserForm1 '※
Set B = UserForm1
Debug.Print A Is B 'Trueと表示される
Set UserForm1 = Nothing
Set B = UserForm1 '※
Debug.Print A Is B 'Falseと表示される
最初にAとBにUserForm1をセットする。
このときは同じインスタンスを参照しているので当然比較すればTrueが返る。
次にUserForm1にNothingを設定してからBを設定し直している。
その後に比較するとBに設定されるのは新たなインスタンスとなり、結果はFalseとなる。
そしてUserForm1のInitializeにDebug.Printを仕込んでおくと※のタイミングで実行される。
Dim MyForm As UserForm1
Set MyForm = New UserForm1
他から参照されて変化されると困る場合はUserForm1を直接使わずにNewして使う方がいい。
広い範囲から同じ見た目のフォームを複数使う場合はPublicに役割別の変数をUserForm1型で宣言してそれぞれにNewして使えばいい。
##ThisWorkbookとSheet
この2つはExportすると普通のclsファイルになってしまうので入れ直すことができない。
Excelのシートなどに対応しているのだから当たり前といえばそう。
ThisWorkbookとSheetはVB_ExposedがTrueとなっている。
ThisWorkbookとSheetはNewできないのでこのExposedがTrueになるとNewできない?
また、これらについては「Set ThisWorkbook = Nothing」のようにNothingを設定することができない(クラスとフォームはできる)
ちなみにクラスでこれをTrueにしていてもインポートしてエクスポートするとFalseにされているので、自作クラスに設定することはできないようだ。
ちなみにThisWorkbookもオブジェクト名(=クラス名)を変えることができるが、変更してもThisWorkbookで同じものを参照できる。
例えばThisWorkbookのオブジェクト名をThisWorkbook2にした場合、ThisWorkbookと入力してCtrl+Jを押すとThisWorkbookとThisWorkbook2が並ぶ。
これは省略しない場合それぞれ、VBAProject.ThisWorkbook2とApplication.ThisWorkbookになると思うが、比較した場合Trueになるので名前や参照方法は違うが同じインスタンス。
ちなみにプロジェクト名を変えた場合は「VBAProject」の部分もそれに応じて変化する。