「Withの功罪」:コードを読みやすくする強力な道具と、その落とし穴
VBAのコードを書くとき、With ステートメントを使うべきか、使わざるべきか。
このテーマは古くから議論されてきましたが、私自身、長年コードを書き、また他人に教える立場を経験する中で、その捉え方が少しずつ変化してきました。
2. 伝える立場になって気づいた「With」の見た目の威力
3. 初心者への推奨:主たるシートを「With」で捉える
4. 「罪」の側:省入力の道具と化したときの違和感
1. 昔の私:Why With? コピペとオブジェクト変数で十分だった
理由は至ってシンプルです。
Worksheets("Sheet1").Range("A1").Value = "商品名"
Worksheets("Sheet1").Range("A1").Font.Size = 11
Dim myRange As Range
Set myRange = Worksheets("Sheet1").Range("A1")
myRange.Value = "商品名"
myRange.Font.Size = 11
わざわざ構文をネストさせてまで With を使う必然性を、当時の私はあまり感じていませんでした。
2. 伝える立場になって気づいた「With」の見た目の威力
With を使うと、ぱっと見でコード全体が非常に読みやすくなるのです。
それはつまり、WEB記事でも見やすくなるという事で、積極的に採用するようになりました。
' Withなし
Worksheets("Sheet1").Range("A1").Value = "商品名"
Worksheets("Sheet1").Range("A1").Font.Bold = True
Worksheets("Sheet1").Range("A1").Interior.Color = vbYellow
' Withあり
With Worksheets("Sheet1").Range("A1")
.Value = "商品名"
.Font.Bold = True
.Interior.Color = vbYellow
End With
ドット(.)で始まる行が連続することで、「あ、ここは同じ対象に対してプロパティを設定しているブロックだな」と直感的に理解できます。
さらに大きなメリットは、そのコードブロックで扱う「主たるオブジェクト」が明確になることです。
執筆者や設計者の「ここをメインに処理します」という意図が、読み手に一瞬で伝わります。
3. 初心者への推奨:主たるシートを「With」で捉える
本来であれば、オブジェクト変数を使って明示するのが定石です。
なお、オブジェクト変数とSetステートメントの基本的な使い方は、こちらの記事を参照してください。
With Worksheets("データ")
.Range("A1").Value = 100
.Range("A2").Value = 200
End With
処理全体を With の中に閉じ込めることで、アクティブなシートがどれであっても誤動作を防ぐことができ、VBAにおいて極めて重要な「常に親オブジェクト(シート)を明示して書く」という感覚を自然と身につけてもらえます。
単に「コードを短く書くため」ではなく、「対象のシートを確実に固定し、安全に操作するため」に With を使う。
これこそが、初心者にとっても分かりやすい With の使い方の一つだと考えています。
- With が適している場面(構造・主役の固定)
処理全体の「主たる舞台」となるシートを1つ固定して操作する場合
セルの書式設定など、同一オブジェクトに対して連続で複数のプロパティを設定する場合 - オブジェクト変数が適している場面(動的・複数要素の操作)
複数のシートやブックをまたいでデータを相互に転送・処理する場合
ループ処理や複雑な条件分岐が挟まり、処理が長くなる場合
With を解体して両方をオブジェクト変数(Set srcWS = ... / Set tgtWS = ...)として宣言した方が、誰の目にも一目瞭然なコードになります。
4. 「罪」の側:省入力の道具と化したときの違和感
それは、With の目的が単なる「入力の省略テクニック」としてばかり捉えられていることに強い違和感があるからです。
「文字数を減らせるから」「打つのが楽だから」という理由だけで使われる With は、時に以下のような悪夢のようなコードを生み出します。
4.1. 過度なネストによる可読性の崩壊
' ネストが深すぎて視認性が落ちるコード例
With Worksheets("Sheet1")
With .Range("A1")
With .Font
.Size = 11
.Bold = True
End With
.Interior.Color = vbYellow
End With
.Range("B1").Value = "完了"
End With
With のネストが深くなるほど、. がどの With の対象を指しているのかを視覚的に追跡する必要が生じ、結果として可読性が急激に落ちてしまいます。
プロパティの設定程度であればネストを増やさず、.Font.Size = 11 や .Font.Bold = True のように記述した方が、ネストによる階層の迷子を防ぎやすくなる場合もあります。
4.2. ブロックの長大化によるスコープの曖昧化
特に危険なのが、左右ともに長い代入文の「右辺の後ろの方」にひっそりとドットが存在するパターンです。
' スコープが見えなくなるコード例
With Worksheets("データ")
.Cells(1, 1).Value = "開始"
' ...(あいだに数十行の異なる処理やループが入る)...
For i = 2 To lastRow
' 左右ともに長い代入文。右辺の奥深くに「.Cells」が隠れている
TargetWorksheet.Cells(i, 5).Value = SourceWorkbook.Worksheets("Master").Cells(i, 3).Value + .Cells(i, 2).Value
Next i
' ...(さらに長い処理)...
End With
上記のようなコードでは、右辺の末尾近くにある .Cells(i, 2) のドットを見落としやすくなります。
「この .Cells はどのシートを指しているのか?」を追うために数十行上までスクロールして With の宣言を確認しなければならず、解読コストが大幅に増えます。
最悪の場合、読み手が「親シートの指定漏れ(タイポ)」と誤解してしまい、バグの特定を遅らせる要因になります。
これを超える長大な処理になる場合は、With で強引にくくるのをやめ、素直にオブジェクト変数(Dim ws As Worksheet 等)へ割り当てて明示的な変数名で操作する方に切り替えるのが安全です。
5. 本当の難しさ:「Withが捉えているオブジェクト」の理解
単なる「文字列の置き換え(短縮)」として使っていると、意図しない挙動や勘違いを引き起こす原因になります。
5.1. 「Withに入った瞬間」にオブジェクトの参照が固定される
With Worksheets("Sheet1").Range("A1")
' このブロック内で評価されるのは、
' With行を通過した瞬間に確定した「Sheet1のA1」という特定のRangeオブジェクト
End With
後から参照先のプロパティや状態が変わっても、With が保持している「オブジェクトへの参照」そのものは変わりません。
5.2. 範囲の変動や操作によって「捉えたオブジェクト」がズレる罠
' A1セルをWithで捉える
With Worksheets("Sheet1").Range("A1")
.Value = "初期値"
' 1行目に新しい行を挿入する
Worksheets("Sheet1").Rows(1).Insert
' ここで設定されるのは、移動後の「A2セル」!
.Value = "変更後"
End With
直感的には「1行挿入されたのだから、もう一度 A1 に書き込まれるのではないか」と誤解しがちです。
しかし、With は「A1 という文字」を覚えているのではなく、With 行が評価された時点で得られた Range オブジェクトへの参照を対象にしています。
そのため、行挿入によってその Range オブジェクト自体がシート上で A2 へ移動すると、With の処理対象もそのまま A2 を書き換えることになります。
5.3. 「Withを使えば高速化する」という誤解と真実
With というステートメント自体が高速化の魔法を持っているわけではありません。
速度差が生じるポイントは、Worksheets("Sheet1") や Cells(i, 1) のようにコレクションからオブジェクトを毎回検索・取得する処理にあります。
' 遅い理由:ループのたびに Worksheets("Sheet1") の検索が発生する
For i = 1 To 10000
Worksheets("Sheet1").Cells(i, 1).Value = i
Next i
' 速い理由:Worksheets("Sheet1") の検索が最初の1回で済むから
With Worksheets("Sheet1")
For i = 1 To 10000
.Cells(i, 1).Value = i
Next i
End With
つまり、速くなっている理由は「With を使ったから」ではなく、「重いオブジェクト参照の呼び出し回数を減らしたから」です。
これはオブジェクト変数に Set ws = Worksheets("Sheet1") と受けて操作するのと本質的に同じメカニズムです。
5.4. デバッグ時の挙動の違い(イミディエイトウィンドウの現象)
' パターン1:式を直接渡した場合 → 参照不可(エラー)
With Worksheets(1).Cells(1, 1)
Stop ' イミディエイトで「?.Value」 → エラー
End With
' パターン2:Variant型変数で受けた場合 → 参照不可(エラー)
Dim rng As Variant
Set rng = Worksheets(1).Cells(1, 1)
With rng
Stop ' イミディエイトで「?.Value」 → エラー
End With
' パターン3:Object型変数で受けた場合 → 参照可能!
Dim rng As Object
Set rng = Worksheets(1).Cells(1, 1)
With rng
Stop ' イミディエイトで「?.Value」 → 値が返る!
End With
' パターン4:Range型変数で受けた場合 → 参照可能&入力候補も出現!
Dim rng As Range
Set rng = Worksheets(1).Cells(1, 1)
With rng
Stop ' イミディエイトで「?.」と打つと入力候補が出現し、「?.Value」も返る!
End With
実機で確認すると、以下のような現象が起こります。
- With Worksheets(1).Cells(1, 1) のように式を直接渡した場合や、Dim rng As Variant で受けた場合は、イミディエイトウィンドウで 「?.Value」 や 「?.Address」 を打ち込んでもエラーになり値を参照できません。
- 一方、Dim rng As Object や Dim rng As Range のようにオブジェクトを扱う型として宣言した変数を経由して With rng とした場合は、イミディエイトウィンドウで 「?.Value」 や 「?.Address」 が正常に受け付けられます。
- さらに Dim rng As Range のように具体的な型を宣言している場合は、イミディエイトウィンドウで 「?.」 と入力した時点でメンバーの自動補完(入力候補)のポップアップまで表示されます。

まとめ:道具の「意図」を理解して使う
罪(デメリット):単なる文字省略として乱用されると、ネストの深化や参照の不透明さを招く。
「短く書けるか」ではなく、「何を主役として扱っているか」で With の使用を判断すること。
コードの主役を際立たせ、読み手に意図を正しく伝えるための構造化のツールとして使ってこそ、その真価を発揮するのです。
まずは「処理の対象となるメインのシートを1つだけ With で囲む」ということから始めてみてください。
それだけで暗黙のシート依存を防ぎ、安全で読みやすいコードになります。
きっと、バグを防ぎワンランク上のコードを書くためのヒントになるはずです。
同じテーマ「マクロVBA技術解説」の記事
印刷範囲の設定・印刷範囲のクリア
VBAの省略可能な記述について
VBAのVariant型について
VBAのインデントについて
VBAの演算子まとめ(演算子の優先順位)
列幅不足による###表示や指数表示を判定する
VBA10大躓きポイント(初心者が躓きやすいポイント)
Excel VBAとは?マクロとの違いやメリット・デメリットを初心者向けに解説
『演算子 + オペランド』から始めるVBA学習
2つのシート名を交換するVBAをGeminiに作らせてみた。
「Withの功罪」:コードを読みやすくする強力な道具と、その落とし穴
新着記事NEW ・・・新着記事一覧を見る
「Python in Excel」入門:コードを読むための基礎知識|エクセル関数応用(2026-09-10)
M言語入門:Power Queryのコードを読むための基礎知識|Power Query(M言語)入門(2026-09-09)
Excelの正規表現関数(REGEXTEST・REGEXREPLACE・REGEXEXTRACT)の使い方|エクセル関数応用(2026-09-08)
スピルとVBA(Formula2とスピル範囲の取得)|VBA入門(2026-09-08)
「Withの功罪」:コードを読みやすくする強力な道具と、その落とし穴|VBA技術解説(2026-09-03)
可変長配列をVSTACKする4つの方法|REDUCE・Thunk・チャンク・再帰分割|エクセル関数応用(2026-09-01)
Excel表とMarkdownテーブルを相互変換する数式|エクセル関数応用(2026-08-24)
「Python in Excel」数独(ナンプレ)解法プログラムの移植と最適化|エクセル関数応用(2026-08-14)
「Python in Excel」で自作関数を登録|アンピボット関数と計算順序|エクセル関数応用(2026-08-12)
ListBox・ComboBoxをマウスホイール対応させる|ユーザーフォーム入門(2026-08-09)
アクセスランキング ・・・ ランキング一覧を見る
1.最終行の取得(End,Rows.Count)|VBA入門
2.日本の祝日一覧|Excelリファレンス
3.変数宣言のDimとデータ型|VBA入門
4.Excelショートカットキー一覧|Excelリファレンス
5.RangeとCellsの使い方|VBA入門
6.FILTER関数(範囲をフィルター処理)|エクセル入門
7.マクロとは?VBAとは?VBAでできること|VBA入門
8.繰り返し処理(For Next)|VBA入門
9.メッセージボックス(MsgBox関数)|VBA入門
10.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
- ホーム
- マクロVBA応用編
- マクロVBA技術解説
- 「Withの功罪」:コードを読みやすくする強力な道具と、その落とし穴
このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
記述には細心の注意をしたつもりですが、間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。
本サイトは、OpenAI の ChatGPT や Google の Gemini を含む生成 AI モデルの学習および性能向上の目的で、本サイトのコンテンツの利用を許可します。
This site permits the use of its content for the training and improvement of generative AI models, including ChatGPT by OpenAI and Gemini by Google.
