t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA Rangeオブジェクトはシートを操作するエージェント

VBAで扱える代表的なExcelのオブジェクトにWorkbook、Worksheet、Rangeなどがある。

  • ひとつのブックにはひとつのWorkbookオブジェクトが対応している。
  • ひとつのシートにはひとつのWorksheetオブジェクトが対応している。

ではひとつのセルに対応する固有のオブジェクトは?



5秒、4、3、2、1、はい。




Cellと答えたあなた。ぶっぶー。

Rangeと答えたあなた。ぶっぶっぶー。

実はひとつのセルに対応する固有のオブジェクトは存在しないってのが答え。

「固有の」ってとこがミソ。

まずCellというオブジェクトは存在せず、WorksheetオブジェクトのCellsプロパティが返すのは全セルを表すRangeオブジェクトである。このことは以下の記事で詳しく書いた。
thom.hateblo.jp

そもそもセルがいくつあるか考えてみよう。Excel2007以降、扱えるセル範囲は16,384桁1,048,576行となっている。つまり単純に掛け算して約170個のセルが存在することになる。これらにそれぞれ対応するオブジェクトが存在するとしたらExcelだけでPCのメモリを食いつぶしてしまう。

じゃあいったいRangeとは何なのか。

Rangeとはシートを操作するエージェント(代理人)のようなものである。

ひとつ実験をしてみよう。
オブジェクトが一致するかどうかを比較する方法として、Is演算がある。
もしSheets(1).Range("A1")がひとつの固有なRangeオブジェクトを返すなら、以下はTrueになるはずだ。

Sub hoge()
    MsgBox Sheets(1).Range("A1") Is Sheets(1).Range("A1")
End Sub

実際に実行してみるとわかるが、上記のマクロはFalseになる。
同じセルを指してるのになんで!?と思うかもしれないけれど、これがエージェントと表現した所以。

つまりRangeとはWorksheetに代わってセルを操作する代理人(エージェント)である。

こんな風に、同じA1セルを管理していても、同じエージェントであるとは限らない。
f:id:t-hom:20170416013829p:plain

だからこうやってIsで比較すると。
f:id:t-hom:20170416014104p:plain

違うんだ!と。
f:id:t-hom:20170416014354p:plain

つまりWorksheetオブジェクトのRangeプロパティは評価するたびに新しいRangeオブジェクトを生成しているのだ。

ちなみに変数に入れてあげれば当然一致する。

Sub hoge()
    Dim r As Range
    Set r = Sheets(1).Range("A1")
    MsgBox r Is r
End Sub

これはTrue。

ここで面白いのが、以下のようにOffsetをかますと不一致になるという点。

Sub hoge()
    Dim r As Range
    Set r = Sheets(1).Range("A1")
    MsgBox r Is r.Offset(0, 0)
End Sub

Offset(0, 0)ということは実質的におなじセルを指してるのに不一致。
つまりOffsetは内部で新しいRangeエージェントを作って返してるようだ。

以前クラスモジュール内で自身と同じ型の別インスタンスを返すという処理をやったけど、まさにあれと同じ。
thom.hateblo.jp

Offsetプロパティは内部でRangeをNewして返してるイメージ。実際に中身見たわけではないので具体的な実装は知らないけど。

なお、同じセルを指しているかどうかを調べたかったらAddressプロパティをイコールで比較してあげればよい。

Sub hoge()
    Dim r As Range
    Set r = Sheets(1).Range("A1")
    MsgBox r.Address = r.Offset(0, 0).Address
End Sub

これはTrueになる。

ちなみにシートの場合はどうかというと、

Sub hoge()
    MsgBox Sheets(1) Is Sheets(1)
End Sub

このように普通にIsで比較してもTrueが返される。

以下のように表現を変えてみても同じくTrue。

Sub hoge()
    MsgBox Sheets(1) Is Sheets("Sheet1")
End Sub

以下のように一旦別シート指してから戻しても同じようにTrueを返す。

Sub hoge()
    Dim sh As Worksheet
    MsgBox Sheets(1) Is Sheets(1).Next.Previous
End Sub

つまりこれが固有オブジェクトであるということ。

ひとつのセルに対応する固有のオブジェクトが存在しないという意味が伝わっただろうか。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。