Withって何ですか?


マクロ記録をすると、たとえば次のようなコードが記録されます。

    With Selection.Interior
        .Pattern = xlSolid
        .PatternColorIndex = xlAutomatic
        .Color = 255
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With

マクロの内容はともかく、今回はこの「With」の話です。Withって、いったい何でしょう?

主語を省略する書き方

Withはステートメントの一種です。マクロ記録で記録されるステートメントは、唯一このWithだけです。Withの働きは、われわれの日常会話を考えれば容易に理解できます。

たとえば、私の友人に「鈴木さん」という人がいたとします。私があなたに、この鈴木さんのことを紹介するとき、次のような言い方はしませんよね。

鈴木さんの出身は横浜で
鈴木さんの家族は4人で
鈴木さんの趣味はテニスで
鈴木さんの特技はギターなんですよ

こんな風に言ったら「鈴木さん、鈴木さんうるせぇよ」と怒られてしまうかもしれません。こんなとき、一般的にわれわれは、次のように言いますよね。

鈴木さんはね
    出身は横浜で
    家族は4人で
    趣味はテニスで
    特技はギター
なんですよ

まず「鈴木さんはね」と宣言することで、これから話す内容は"鈴木さん"に関することであるという暗黙の取り決めをします。だから、次の「出身は横浜で」と言えば、これは"鈴木さん"の出身であると相手も理解してくれます。このように、何度も登場する主語を先に宣言しておき、その後の会話では主語を省略するときに使うのがWithステートメントです。上の会話をWithを使って表すと、次のようになります。


With 鈴木さん
    出身は横浜で
    家族は4人で
    趣味はテニスで
    特技はギター
End With

最後の「End With」は、もう鈴木さんの話はここで終わりですよ、というような意味です。これを、もう少しVBAぽく書くと次のようになります。

With 鈴木さん
    の出身は横浜で
    の家族は4人で
    の趣味はテニスで
    の特技はギター
End With

「の出身」というのは、「○○の出身」の「○○」つまり主語が省略されているという意味です。

では、Excelの操作で考えてみましょう。たとえば「セルA1に"tanaka"を代入して」「セルA1の背景色を赤くして」「セルA1の文字の色を白にして」「セルA1に文字列の表示形式を設定する」というマクロを作るとします。最初の、ウザい言い方をするなら

セルA1の値に"tanaka"を代入して
セルA1の背景色を赤くして
セルA1の文字の色を白にして
セルA1に文字列の表示形式を設定する

となります。「セルA1、セルA1って何度もうるせぇよ」って感じです。実際にVBAのコードで書くと次のようになります。

Range("A1").Value = "tanaka"
Range("A1").Interior.ColorIndex = 3
Range("A1").Font.ColorIndex = 2
Range("A1").NumberFormat = "@"

こうしたとき、Withを使って主語(操作の対象)を省略することができます。

セルA1のね
    値に"tanaka"を代入して
    背景色を赤くして
    文字の色を白にして
    文字列の表示形式を設定して
くださいね

つまり、

With セルA1
    の値に"tanaka"を代入して
    の背景色を赤くして
    の文字の色を白にして
    に文字列の表示形式を設定して
End With

したがって

With Range("A1")
    .Value = "tanaka"
    .Interior.ColorIndex = 3
    .Font.ColorIndex = 2
    .NumberFormat = "@"
End With

と書けるわけです。

ここでポイントは、With~End Withの間で主語(操作の対象)を省略したときは、それを明示するためにピリオドから書き始めるということです。言い換えると、ピリオドで始まっているオブジェクト式は、Withでくくったものが操作の対象になっているわけです。

誤解している人が多いのですが。WithとEnd Withの間では「必ず行頭がピリオドで始まる」わけではありません。WithとEnd Withの間にある「先頭がピリオドで始まっているオブジェクト式は対象(主語)が省略されている」ということです。したがって、次のような書き方もできます。

Sheets("Sheet1").Activate
With Sheets("Sheet2")
    Range("A1") = .Range("B2")
End With

上記のマクロを実行すると何が起きるか分かりますか。1行目で「Sheets("Sheet1").Activate」を行っていますので、アクティブシートは[Sheet1]になります。「Range("A1")」はワークシートを省略していますので、アクティブシートのセルA1という意味です。つまり、[Sheet1]のセルA1ですね。そこに「.Range("B2")」を代入しています。これはピリオドで始まっていますので、直前のWithでくくった[Sheet2]のセルB2を表します。

これが、Withステートメントの働きです。

最後に、少しだけVBAの仕組み的な話をします。意味が分からなければ、読み飛ばしてください(笑)。

Withステートメントがコンパイルされるとき、内部ではVBAがオブジェクト変数を用意して、そこにWithステートメントでくくったオブジェクトを格納します。ピリオドで始まる命令は、内部で確保したオブジェクト変数に対して行い、End Withが実行されると、確保したオブジェクト変数を開放します。

With Range("A1")
    .Value = 100
End With

というコードは、実際には次のようにコンパイルされます。

Dim xxx As Range
Set xxx = Range("A1")
xxx.Value = 100
Set xxx = Nothing

「xxx」は、VBAが内部で使用するオブジェクト変数の名前(のつもり)です。実際には、どんな名前が使われるか、われわれユーザーには分かりません。