VBAに出てくるクラスとして扱われるものについて

#主な話
1. VBAでクラスに分類されるもの
2. PredeclaredIdって何してるの
3. ThisWorkbookとSheet

経験則で書いてるので間違っていることあれば指摘お願いします。

##VBAでクラスに分類されるもの
[最初の記事](https://qiita.com/towa_skm/items/667424c972001a27fcfc)でちょろっと触れたが、ユーザーフォームはクラス。
他にもThisWorkbookやSheetもクラスだがこいつらはフォームより特殊でNewできなかったり、既定のインスタンスにNothingを設定できなかったりする。
エクスポートして中身をテキストエディタで見るそれぞれこうなる。

```visualbasic:ThisWorkbook.cls
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
```
```visualbasic:Sheet1.cls
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
```
```visualbasic:UserForm1.frm
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は何をしているのか
```visualbasic
'普通のクラスの使い方
Dim ClassDim As Class1
Set ClassDim = New Class1
Call ClassDim.Method
'PredeclaredId = Trueにしたクラスの使い方
Call Class1.Method
```

このようにインスタンスを作らないでもクラス名からメソッドを呼べるというのが特徴。
なのだが、実際のところやってるのは標準モジュールで以下のように宣言してるのと大差ない。

```visualbasic:Module1.bas
Option Explicit
Public Class1 As New Class1
```
なぜかと言うとPredeclaredId = Trueに設定したクラスは最初に呼び出されたときにInitializeが働く。
つまり最初に参照されたときにNewされているので自動的にクラス名と同名の変数がAs Newで宣言されるような設定ということ。
厳密にはなにか違うのかもしれないが、わざわざ取り出して設定して入れ直して……というのが手間だと思うならこれも手だと思う。
結局Nothingと比較しても常にFalseになることや、意図しないタイミングでInitializeが走るという点では変わらないので……。

###ユーザーフォームで確認してみる
```visualbasic:ユーザーフォームのテスト
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を仕込んでおくと※のタイミングで実行される。

```visualbasic
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」の部分もそれに応じて変化する。