🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA の With ステートメント — オブジェクトを一度だけ書く(そして成否を分ける先頭のドット)

|

Excel VBA の With ステートメント — オブジェクトを一度だけ書く(そして成否を分ける先頭のドット)

TL;DRWith object … End With は、オブジェクトに一度だけ名前を付け、 そのメンバーに先頭のドットだけで届かせてくれます:.Value.Font.Bold.Interior.Color。打つ量が減り、読みやすく、そして本当に速くなります。 VBA がオブジェクト参照を毎行ではなく一度だけ解決するからです。仕掛けのすべては、 その先頭のドットに宿っています:.Font はあなたの With オブジェクトに バインドし、ドットなしの Font はそうならない — それは暗黙のオブジェクト (たいていアクティブシート)にフォールバックし、無言で間違ったものを変えて しまいます。

With Sheets("Report").Range("A1")
    .Value = "Total"          ' 各ドットの意味:Sheets("Report").Range("A1") . ...
    .Font.Bold = True
    .Font.Size = 14
    .Interior.Color = vbYellow
End With

深いオブジェクトのパスは、Excel VBA のあちこちにあります: ThisWorkbook.Worksheets("Data").Range("A1").Font.…。この接頭辞を 10 行連続で 打つのは、退屈で、実行も遅く、読みにくいものです — そして別のセルに向け直したく なったら、10 か所を編集することになります。With はこの 3 つを一度に解決します: 最初の行でオブジェクトを述べ、あとはブロック内の各行をドットでそこにぶら下げるの です。この言語で最も単純な構文の 1 つでありながら、微妙に間違えやすいものの 1 つ でもあります。

この記事で学べること

  • 考え方の軸 — With は、代数の 2(a+b) のように、くり返す接頭辞をくくり出すこと
  • ただ見た目が整うだけではない理由:With はオブジェクト参照を一度だけ解決すること
  • ドット抜けの罠 — Font.Font — そしてなぜそれが無言で失敗するのか
  • 入れ子の With ブロックがドットをどう解決するのか、そしてなぜ 2 つで止めるべきか
  • このまとまりを束ねる With + For Each の定石

考え方の軸:くり返す接頭辞をくくり出す

With を、2*a + 2*b + 2*c2*(a + b + c) と書き直すときの「くくり出し」の ように考えてください。くり返される部分 — ここではオブジェクトのパス Sheets("Report").Range("A1") — を一度だけ述べ、括弧の中のすべてはそれが掛けられて いると理解されます。VBA では「括弧」が With … End With で、「それを掛ける」が 先頭のドットです。

' With なし — 接頭辞が毎行くり返される
Sheets("Report").Range("A1").Value = "Total"
Sheets("Report").Range("A1").Font.Bold = True
Sheets("Report").Range("A1").Font.Size = 14

' With あり — 一度だけ書いて、ドットで入り込む
With Sheets("Report").Range("A1")
    .Value = "Total"
    .Font.Bold = True
    .Font.Size = 14
End With

どちらもまったく同じことをします。2 つ目は短く、1 回の編集で向け直せ、そして — 次のセクションで示すように — 実際に速く走ります。ブロック内のすべては、With 行のオブジェクトを暗黙の接頭辞として持ちます。

見た目だけの話ではないと分かるルール:オブジェクトは一度だけ解決される

With は、打つ量を減らすだけの話ではありません。VBA が Sheets("Report").Range("A1").Font を 10 の別々の行で目にすると、その連鎖全体を 10 回たどり直します — シートを引き、範囲を解決し、フォントを取ってくる — その 一行ごとに。With ブロックの中では、VBA はオブジェクトを、入るときに一度だけ 解決し、その解決済みの参照をドット付きの各メンバーで使い回します。

数行なら違いは目に見えません。何千ものセルにわたるループの中では、それは現実に なります。

Dim cell As Range
For Each cell In Range("A1:A10000")
    With cell
        .Value = .Value * 1.1       ' 'cell' の解決は反復ごとに一度…
        .NumberFormat = "0.00"      ' …三度ではない
        .Font.Color = vbBlue
    End With
Next cell

With がなければ、各 cell. は毎行その範囲オブジェクトを参照解決し直します。 With があれば、その参照解決は反復ごとに一度だけ起きます。ルールはこうです: 同じオブジェクトのメンバーに 3 つ以上触れるとき — とくにループの中では — それらを With で包む。 読みやすさの改善が同時に速度も買ってくれる、数少ない例です。

別のオブジェクトを変えてしまう罠:先頭ドットの抜け

これは、With を単に便利なものではなく危険なものに変えるバグです。先頭のドット こそが、メンバーを With オブジェクトに結びつけます。それを外すと、メンバーは エラーになりません — 別の何かにバインドされるのです。

With Sheets("Report").Range("A1")
    .Value = "Total"
    Font.Bold = True        ' バグ:先頭ドットなし — これは With オブジェクトのフォントではない
End With

.Font.BoldSheets("Report").Range("A1").Font.Bold を意味します。しかし Font.Bold — ドットなし — は修飾されていない参照です。VBA はそれをアクティブ シートの暗黙のコンテキストに対して解決するので、コンパイルエラーになるか、もっと 悪いことに、たまたまアクティブなシート上のセルを無言で書式設定してしまいます。 何も警告してくれません。With オブジェクトは手つかずのまま、別のオブジェクトが 変わるのです。

ルールは機械的で、頭に焼き付ける価値があります:With ブロックの中では、その オブジェクトに属するメンバーはすべてドットで始まらなければならない。 With ブロックをレビューするときは、左端を目で追ってください — . で始まっていない ものは、別のオブジェクトへの意図的な参照か、さもなくばバグです。(これは VBA ActiveSheet で扱う「暗黙の ActiveSheet」ミスと 同じ系統です — 修飾されていない RangeCells は、たまたまアクティブなシート に静かにくっつきます。)

入れ子のルール:ドットは最も近い With にバインドする

With ブロックは入れ子にでき、サブオブジェクトへ掘り下げるのに実際に役立ちます。 しかし解決のルールが人を引っかけます:先頭のドットは、つねに最も内側With にバインドします。

With Sheets("Report")
    .Range("A1").Value = "Report"      ' ドット 1 つ -> シート
    With .Range("A2").Font             ' フォントに対する内側の With を開く
        .Bold = True                   ' .Bold -> フォント(最も内側)、シートではない
        .Size = 12
    End With
    .Range("A3").Value = "Done"        ' End With のあとはシートに戻る
End With

内側のブロックの中では、.Bold はフォントのものであって、シートのものでは ありません。2 段までなら正しく読みやすい — しかし 3 段や 4 段になると、各ドットが どのオブジェクトを指すのかの当てっこになってしまいます。ルールはこうです: With ブロックの入れ子は最大 2 つまで。 それより深くなるなら、中間のオブジェクト 変数に代入し(Set fnt = .Range("A2").Font)、ドットの積み重ねの代わりに、 読み手に名前を与えましょう。

このまとまりを束ねる定石:With + For Each

WithFor Each は自然な相棒です。For Each は オブジェクトを 1 つずつ手渡し、With はループ変数をくり返すことなくそのオブジェクト をすっきり扱わせてくれます。

Dim cell As Range
For Each cell In Sheets("Data").Range("B2:B100")
    With cell
        If .Value < 0 Then
            .Interior.Color = vbRed
            .Font.Bold = True
        End If
    End With
Next cell

声に出して読むと、ほとんど日本語のままです:「各セルについて、そのセルで — もし 負なら、赤く塗って太字にする」。この組み合わせ — コレクションを歩くための For Each、各メンバーに作用するための With — は、Range 全体にわたる、すっきりした書式設定やデータ標示のマクロの背骨です。

ExcelMaster の活用

With は、千回くり返される小さなことです:接頭辞をくくり出し、ドットを忘れず、 深く入れ子にしすぎない。これはまさに、読みやすいマクロと脆いマクロを分ける機械的な 心配りであり — そして、ただシートを書式設定したいだけのときに、できれば考えたく ない類のことです。

ExcelMaster なら、 代わりに結果を説明できます。「Data シートで、B 列の負の値をすべて赤くして太字に して」と言えば、そのロジックを書いて実行します — すべてのオブジェクト参照を正しく 修飾するので、ドット抜けが引き起こす無言の「間違ったシート」バグは、そもそも起き ようがありません。定期実行するマクロを手書きするときは、これからも自分で With に 手を伸ばすことになるでしょう。しかし日々の書式設定と標示の作業なら、望むものを 説明する方が、先頭のドットを一つひとつ手で正しく打つより勝るのです。

よくある質問

VBA の With ステートメントは何をしますか?

With object … End With はオブジェクトに一度だけ名前を付け、そのメンバーに、 フルのオブジェクトパスを毎行くり返す代わりに、先頭のドットだけ(.Value.Font.Bold)でアクセスできるようにします。コードは短く、向け直しやすく、そして 速くなります — VBA がオブジェクト参照を、各行でではなく入るときに一度だけ解決する からです。

With ブロックが正しいオブジェクトを変えてくれないのはなぜですか?

ほぼいつも、先頭のドットの抜けです。With obj の中で .Fontobj を指し ますが、ドットなしの Font は修飾されていない参照で、アクティブシートの暗黙の コンテキストにバインドします — だから無言で間違ったオブジェクトを変えてしまうの です。With オブジェクトに属するメンバーはすべて、ドットで始まらなければなりません。

With ステートメントは本当に VBA を速くしますか?

はい、控えめにですが、ループの中では効いてきます。VBA は With 行でオブジェクトを 一度だけ解決し、その参照をドット付きの各メンバーで使い回します。フルのオブジェクト 連鎖を各行でたどり直す代わりにです。2、3 行では気づきませんが、何千回もの反復に わたれば、省かれた参照解決が積み上がります。

VBA で With ステートメントを入れ子にできますか?

はい。先頭のドットはつねに最も内側の有効な With にバインドするので、 With Sheets("Report") の中の With .Range("A2").Font.Bold をフォントの ものにします。2 段までは読みやすいですが、それより深い入れ子は、各ドットがどの オブジェクトを指すのか分かりにくくします — 3 段以上入れ子にする代わりに、中間の オブジェクト変数に代入しましょう。

ブロックの中で With オブジェクトそのものを変えられますか?

いいえ — With の参照は、ブロックに入った時点で固定されます。With が指す先を 途中で付け替えることはできません。別のオブジェクトを扱うには、End With で ブロックを閉じ、新しいものを開きます。オブジェクトのメンバーを読み・書きするのは 自由ですが、ブロックの指す先を変えることだけはできません。

検証環境

検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-01。

関連ガイド: VBA For Each · VBA Collection · VBA Range · VBA ActiveSheet · VBA For Loop