VBA用語の再入門・Rangeの内部動作

下の記事から分割してきました。

https://qiita.com/sasio/items/2b82b92fd95c63c87063

# VBA関連用語
凡例：広い>狭い

## VBAとは
- VBA>ExcelVBA
    - VBA：プログラミング言語名
    - ExcelVBA：Excelライブラリを参照して、セルの操作ができるようになったVBA

## マクロは機能名
- Excel>マクロ
    - （新）マクロ：1つ以上のプロシージャからなる実行の流れ。
        - Excel>開発>マクロの一覧で選べるやつ。
        - ExcelVBAで記述される。
    - （旧）マクロ：「Excel4.0マクロ」
        - ほぼ使わない。

## クラス定義の記述
ライブラリ>クラス>メンバー

メンバーとは、プロパティやメソッドなどのこと。ドット記法で使用する。
Excel固有のクラス(Range,Sheetなど)は、Excelライブラリで定義され、
シートやセルへできること(CopyやValueなど)の動作が記述されている。
Excelライブラリ>Rangeクラス>Valueプロパティ

## オブジェクトの内部的な階層
アプリケーション>ワークブック>シート>セル
Excel>Book>Sheet>A1

## Excel固有のオブジェクト
Range,Sheetなどのオブジェクトは、クラスを基に生成された実体(インスタンス)である。
各オブジェクトに対するプロパティなどの処理は、各クラスで定義される。
オブジェクトモデルの考え方に基づく。

## Excel側のオブジェクト管理
Dim Rangeでは、メモリ内に「Range->{Value,Font,...}」がその都度確保されるわけでなく、エクセル側が管理しているセルへの参照が確立するだけ。

実際の管理状況に関しては、
ワークブックとシートは、固有オブジェクトと紐づいており、
使用時はこのオブジェクトを指す。
セルは、各セルごとに固有のオブジェクトは存在しない。
シートを作成しただけで数億のオブジェクトが生成されるわけではなく、
各セルの使用時に初めて指す。


## コードの階層
- アプリケーション>プロジェクト>モジュール>メンバー>ステートメント>論理行>物理行>(>右辺値)
    - アプリケーション：1つのプログラムとして動作するコードの集合。
    - プロジェクト：モジュールの集合。
    - モジュール：標準モジュールなど。テキストファイルっぽいやつ
    - メンバー：モジュールをオブジェクトと捉え、Rangeなどに対するメンバーと同様に扱う。メソッドなど
    - （モジュールの）メソッド：そのモジュールに紐づくプロシージャ
    - プロシージャ：他言語のmain()みたいなやつの宣言全体
        - プロパティやメソッドの各記述を指す。ライブラリ内も含む。
        - また、呼び出しはCallステートメントやRunメソッド
    - ステートメント：1構文のこと。Dim,Let,With,For-Next,Callなど
        - 「For i = 0 To 1: hoge: Next」の組が1つのステートメントで、hoge自体もステートメント。hogeには1つ以上のステートメントが入る。
        - Cellと見做すことにより、オブジェクトのメソッドも含まれる。
    - 論理行：行連結文字（スペース&アンダースコア）を止めるまで。
    - 物理行：改行するまで。
    - 右辺値：値を返すプロパティや、変数、リテラル、VBA関数(IsNullなど)
    - 関数：右辺値の中で、値を返すプロシージャのうち汎用的に使用できるもの。
        - Functionプロシージャの呼び出し、VBA関数、ワークシート関数など
        - Subプロシージャの呼び出しは、値を返さないため除く

https://thom.hateblo.jp/entry/2017/01/29/101605

# 省略記法
- マルチステートメントの:コロン

## 省略可能なキーワード
- a = 1は、Let a = 1の省略
- 返り値のないメソッドやプロシージャの呼び出しは、Call <呼び出し>の省略

## 糖衣構文
- Evaluateメソッドの、[ ]角括弧
- Remステートメント(コメント文)の、'アポストロフィー

# オブジェクト
## オブジェクト変数の型
- Integerなどと違い、ObjectやVariantはサイズが可変
    - Object型で確保した変数に、Rangeやシートなどへの参照を保存
        - Object/Range, Object/Sheet1など
    - Variant型で確保した変数に、Object型を保存してRangeを保存
        - Variant/Integer, Variant/Object/Rangeなど

## クラスとオブジェクトとプロパティ
SheetやRangeなどのお馴染みの面々は、さまざまな肩書きがある。

- ①Rangeクラス：プロパティやメソッドの記述
    - シートやセルへできること(CopyやValueなど)の動作が記述されている
- ②Rangeオブジェクト：クラスの実体(インスタンス)
    - クラスを基に生成され、エクセル側で管理される
    - 操作できそうなもの(Book,Sheet,Rangeなど)は全てオブジェクトである
    - Dim As hogeで指定するやつ
        - コード内においては、Rangeオブジェクトを指すオブジェクト変数を宣言するのであって、Rangeオブジェクトを作るのではない
        - Dimが本来受け取る指定はクラス名だが、Excel固有系を対象としたときはオブジェクト名と呼ぶっぽい。参考：[VBE用語集 - Microsoft](https://docs.microsoft.com/ja-jp/office/vba/language/glossary/vbe-glossary#object-type)
- ③Rangeプロパティ：コード内の関数っぽいやつ
    - 上位オブジェクトのメンバーの1つ
    - Global.Application.Workbooks(1).Sheets(1).Cells(1,1).Valueでは、
    - Applicationは、Globalのプロパティで、Applicationへの参照を返す
        - 後述する
- ④Range（オブジェクト型）：操作するオブジェクトへのアドレス参照
    - Range("A1")などのプロパティの、評価結果としての肩書
    - メモリ内に「Range->{Value,Font,...}」がその都度確保されるわけでなく、エクセル側が管理しているセルへの参照が確立するだけ
    - 単に「Rangeオブジェクト」などと言うと、②ではなくこちらを指すが、厳密には「Rangeオブジェクトを表す式」と呼ぶ
- 「オブジェクト型」はふわっとしてるので文脈判断
    - 値型の対である参照型の別名。Variant/Objectも含む
    - Rangeなど、VBにはないExcel固有のクラスの総称
    - 任意のObjectへの参照をもつVBのデータ型の変数。ActiveXなども含む
    - メモリの確保がObject型となる式の評価結果。Object/hoge

## 式の評価過程
Cells()を教材用としてわざわざ長めに、
Excel.Global.Application.Workbooks.Item(1).Sheets.Item(1).Cells.Item(1,1).Value
と書けなくもない。これが左から評価される。
例示については、↓がわかりやすい。

https://thom.hateblo.jp/entry/2016/05/25/140304

<details><summary>自分なりの説明</summary><div>

- Excelは、ライブラリ
- Globalは、Excelライブラリ内のクラス
- Applicationは、Globalクラスのプロパティで、Applicationオブジェクトを返す
    - \<Excel.Global.Application>までがApplicationオブジェクト扱いになる
    - myobject.Applicationのようなオブジェクト修飾子がないときは、Excelアプリケーション自体を返す
    - Applicationオブジェクトは、Applicationクラスの実体なので、Applicationクラスのプロパティが使える
- Workbooksは、Applicationクラスのプロパティで、Applicationオブジェクトを取り、Workbooksコレクションを返す
    - \<~tion.Workbooks>までがWorkbooksコレクション扱いになる
- Itemは、Workbooksクラスのプロパティで、Workbooksコレクションを取り、Workbookオブジェクトを返す
    - Itemはいろんなクラス内にあるプロパティだが、この位置なのでWorkbooksクラスのプロパティだとわかる
- Sheetsは、Workbookクラスのプロパティで、Workbookオブジェクトを取り、Sheetsコレクションを返す
- Itemは、Sheetsクラスのプロパティで、Sheetsコレクションを取り、Sheetオブジェクトを返す
- Cellsは、Sheetクラスのプロパティで、Sheetオブジェクトを取り、Rangeオブジェクトを返す
    - いろんなクラス内にあるが、この位置なのでSheetクラス
    - そのシートのセル全体を、セル範囲として返す
        - 1セル(A1)、セル範囲(A1:A2)、複数セル領域(A1:B2,D4:E5)は、全てRangeオブジェクト
        - ちなみに、列(A:B)、行(1:2)も、Rangeオブジェクト
- Itemは、Rangeクラスのプロパティで、Rangeオブジェクトを取り、Rangeオブジェクトを返す
    - 指定位置の1セルを返す
- Valueは、Rangeクラスのプロパティで、Rangeオブジェクトを取り、Variant型でセルの値を返す
</div></details>


## オブジェクトとコレクション
WorkbooksとWorkbookとWorksheetsとWorksheetは、全てオブジェクトである。
そのうちWorkbooksとWorksheetsは、コレクションとも呼ばれる。

## 既定メンバー
Set Range()だとオブジェクトへの参照のまま保存する。
Let Range()などだと値を保存するため、右辺にはプロパティやメソッドでの値が望まれる。
そのときの既定メンバーとして、_Defalutプロパティがある。
Range("A1").[_Defalut]が呼び出され、
_DefalutはValueなどの別プロパティへの転送を行う。

## グローバルクラス
Range()はプロパティなので、本来はオブジェクト修飾子が必要。
Worksheetへの記述だと、Me.Range()と同等に補完される。
ModuleやWorkbookへの記述だと、紐づいているシートはないが、
Globalクラスに記載があるため、Excel.Global.Range()と補完される。
(Globalクラスに記載がなければ、ライブラリ名から正確に書く必要があった)
これはExcel.Application.Range()に変化する。
Application.Rangeにおいて、シートなどのオブジェクト指定で分岐し、
("Sheet1!A1")はApplication.Range()のまま実行、("A1")はActiveSheet.Range()に転送される。
（転送といっても、シート選択するだけで、sheet.Rangeの結果型にはならない。後述する）
つまり、Excel.Application.ActiveWorkbook.ActiveSheet.Range()となる。

Cellsもプロパティだが、
Worksheetへの記述だと、Me.Cellsと補完される。
ModuleやWorkbookへの記述だと、Excel.Global.Cellsと補完される。
これはExcel.Application.Cellsに変化する。
Application.Cellsにおいてオブジェクトの確認はなく、ActiveSheet.Cellsに転送される。
つまり、Excel.Application.ActiveWorkbook.ActiveSheet.Cellsの一択となる。

補完候補順は、↓の2つが詳しい。

https://qiita.com/nukie_53/items/4e9226ac8f747e772afd

https://www.excel-chunchun.com/entry/disable-global-vba

上記と同様に、Dim As RangeもDim As Excel.Rangeとなる。

## オブジェクト修飾子の省略
実際の補完内容は、オブジェクトブラウザーを用いて確認できる。
オブジェクトブラウザーについてはこちら。

https://www.relief.jp/docs/vbe-definition-icon-object-browser.html

あらかじめ、オブジェクトブラウザーを一度開き、
ウィンドウ内で右クリック＞非表示のメンバーを表示、をしておく。
上位のオブジェクトに関しては、
コード内の調べたい記述上で右クリック＞定義
すると、定義の記述へ飛ぶが、その参照先が
自作ではなくライブラリ内の場合は、オブジェクトブラウザーへ飛ぶ。
下位の既定メンバーに関しては、
オブジェクトブラウザーで、青丸アイコンのメンバーを探す。
ここ↓を参照。

https://stackoverflow.com/questions/37239743/returning-the-default-property/44401288

ここで言う上位オブジェクトとは
使おうとしているメンバーが、どのクラスで定義されているかである。
オブジェクト階層構造での親ではない。
例として、
ActiveSheetは、Application.ActiveSheetなので、
Applicationクラスのメソッドである。
評価結果としては、WorkSheetオブジェクトなので、
.Parentで求まる親オブジェクトは、WorkBookオブジェクトである。
階層構造の関係図は、こちらを参照。(2010のもの)

https://euc-access-excel-db.com/tips/ct07_se/ct075012_xls2k_vba_tips/make-current-obuject-siki-01

## Rangeオブジェクト.Itemプロパティの動作
- Rangeオブジェクトの_Defalutの転送先は、引数があればItem/なければValue
    - Range("A1")は、Range("A1").Value
    - Cellsは、Cells.Value
    - Cells(1,1)は、Cells.Item(1,1)で、Cells.Item(1,1).Value
    - ちなみにSheets()は、Sheets.Item()が既定
- セルに対する.Itemは、最初のセル領域に対して計算し、1セルを返す。
    - Range("A1:B2,D4:E5").Item()なら、A1:B2に対して作用する。
    - [VBAクイズ 問1～問5の解説 Excel VBAのRange操作の注意事項 - えくせるちゅんちゅん](https://www.excel-chunchun.com/entry/2019/01/19/013140) が、わかりやすい。
        - Itemの引数が2個なら、対象領域の左上を基準(1,1)とした1セルを返す。0以下も使える。
            - これがいつものsheet.Cells(a,b)。全セルの左上A1を基準として1セルを返す。0以下はシート範囲外でエラー。
        - Itemの引数が1個かつ対象領域がセル範囲なら、対象範囲の左上を基準(1)として、範囲内を右方向を優先として右下に数えた番号で示す。範囲外も幅を保って下に延びる。0以下は、基準セルを中心とした点対称な領域が左上に延びてるとして、左方向を優先に左上に数える。
        - Itemの引数が1個かつ対象領域が1セルなら、そのセルを基準(1)として上下に延びる。

## 式の強制評価
Range("A1")をLet強制以外で値にする方法2つ
(Range("A1")), Range("A1")()

(hoge)では、(1+2)*3と同じく、式の評価を優先的に行う。
hoge()では、0個の引数を取ることで、オブジェクトのままではなくメンバーの呼び出しだとと明示される。
参考↓

https://akashi-keirin.hatenablog.com/entry/2020/12/19/151516

# 変更履歴
2021/7/18 初稿
2021/8/5 見出し整理