TL;DR —
Intersectは、2 つの範囲が共有するセルだけを返します。触れ合わなければNothingを返します — 空の範囲でもエラーでもなく — そしてそのNothingこそが肝心な点 です。一番の用途はWorksheet_Changeイベントをガードして、気にかける列の編集にだけ反応 させることです。最も大事なルール:重なりがなければNothingなので、Is Nothingの確認 なしにどんなプロパティへ触れてもerror 91でクラッシュします。
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Me.Range("B:B")) Is Nothing Then Exit Sub ' 編集は B 列ではなかった
Application.EnableEvents = False ' 自分の書き込みでこれが再発火しないように
Target.Offset(0, 1).Value = Now ' 隣に時刻を刻む
Application.EnableEvents = True
End Sub
Union は範囲をまとめます。Intersect はその正反対で — 共有部分へ縮めます。そして
それ以前のどの参照とも違い、Intersect はまったく何も返さないことが正当にありえます。
重ならない 2 つの範囲には共通のセルがないからです。本記事は 1 つの考えを軸にしています。
Intersect は「これらは重なるか?」という yes/no の問いに、重なりか Nothing のどちらかを
返して答える。だから触れる前に、どちらなのかを判定しなければならない。 その 1 つの習慣が、
堅牢なイベントハンドラーと、間違ったクリックでクラッシュするものとを分けます。
この記事で学べること
- 考え方の軸 —
Intersectは 2 つの範囲の重なり、外れればNothingを返す - 最も大事なルール — 重なりがなければ
Nothing、だからIs Nothingでガードしないとerror 91 - 決定的な用途 —
Worksheet_Changeのガードと、それに付いてくるEnableEventsの罠 Intersect(Selection, ...)でマクロを対象エリアに限定する- なぜ
Intersect(∩、重なり)は Union(∪、まとめる)の正反対なのか - 複数範囲の Intersect と、同じシートのルール
考え方の軸:Intersect は 2 つの範囲の重なり
Intersect に 2 つの範囲を渡すと、両方にあるセルだけへの参照を返します。
Dim shared As Range
Set shared = Intersect(Range("A1:D10"), Range("C5:F20"))
' shared は C5:D10 - 2 つの範囲が共通して持つ長方形
2 つの範囲が何も共有しなければ、返すべき共通の長方形がないので、Intersect は Nothing を
返します。それこそが、これを便利にする振る舞いです。重なりのセルそのものを気にかけることは
めったになく、そもそも重なりがあるかどうかを気にかけるのです。「このセルはあの領域の中に
あるか?」は、「Intersect(cell, region) は Nothing ではなく何かか?」になります。Intersect
は幾何の問いを、単純な有無の判定に変えます。
最も大事なルール:重なりがなければ Nothing が返る
外れると Nothing が返るので、確認せずに結果に対してどんなプロパティやメソッドへ手を
伸ばしても、それはクラッシュ待ちです。
' 脆い書き方 - 範囲が重ならない瞬間に error 91:
MsgBox Intersect(Selection, Range("B:B")).Address
Selection が B 列にないとき、Intersect は Nothing で、Nothing に対する .Address は
error 91「オブジェクト変数または With ブロック変数が設定されていません」を発生させます。
修正は必ず Is Nothing で先に判定することで、慣用的な言い回しは「重なりがあるなら」を
意味する If Not ... Is Nothing を使います。
If Not Intersect(Selection, Range("B:B")) Is Nothing Then
MsgBox "Your selection touches column B."
End If
If Not (重なり) Is Nothing は「重なりが Nothing でないなら」— つまり、それが存在するなら
— と読みます。ここでの 2 つの間違いは、Not を落とすこと(論理が反転する)と、Is Nothing の
確認を落とすこと(外れたときクラッシュする)です。この一節を丸ごと覚えましょう。VBA 全体でも
最も多く打たれる行の 1 つです。
決定的な用途:Worksheet_Change のガード
ここで Intersect はその存在価値を示します。Worksheet_Change イベントは、シート上のどこ
の編集でも発火し、99% の場合、あなたは 1 つの列や領域の変更にだけ反応したいはずです。ガードが
なければ、ハンドラーはシート全体のあらゆるキー入力で走ります — 遅くて邪魔です。Intersect が
そのガードです。変更されたセル(Target)が、監視している範囲と重なるかを尋ねます。
Private Sub Worksheet_Change(ByVal Target As Range)
' 編集がデータ範囲 B2:B1000 に入ったときだけ動く:
If Intersect(Target, Me.Range("B2:B1000")) Is Nothing Then Exit Sub
Application.EnableEvents = False ' 重要 - 下記参照
On Error GoTo CleanExit
Dim c As Range
For Each c In Intersect(Target, Me.Range("B2:B1000")).Cells
c.Offset(0, 1).Value = "edited " & Format(Now, "yyyy-mm-dd hh:nn")
Next c
CleanExit:
Application.EnableEvents = True ' エラーのあとでも必ず戻す
End Sub
これを正しいパターンにしているのは 2 つです。第一に、ガード行 If Intersect(...) Is Nothing Then Exit Sub が、対象の外のどんな編集でも早々に抜けるので、本体は走るべきときにだけ走ります。
第二に — そしてこれが誰もを捕まえる罠ですが — ハンドラーはセルへ書き込み、セルへの書き込みは
Worksheet_Change を再び発火させ、それがまた書き込み、と続きます。Application.EnableEvents = False がその再帰を断ち切り、それは CleanExit ラベルの中で戻さねばなりません。さもないと
ハンドラー途中のエラーが、そのセッションの残りずっとイベントを無効のまま残します(VBA On Error
と Worksheet_Change を参照)。Intersect のガードと EnableEvents
のガードは兄弟です — 片方だけが欲しいことは、まずありません。
マクロを対象エリアに限定する
同じ重なりの判定は、普通のマクロを 1 つの領域に絞ります。ある書式設定マクロが、ユーザーが 印刷範囲の中で選んだセルだけに触れるべきだとします — 選択と領域を intersect し、その結果を 操作します。
Dim scope As Range
Set scope = Intersect(Selection, Range("PrintArea"))
If scope Is Nothing Then
MsgBox "Select cells inside the print area first."
Exit Sub
End If
scope.Font.Bold = True ' PrintArea の中に入る、選択されたセルだけ
これは、選択された各セルのアドレスを手で確かめるより、すっきりしたパターンです。Intersect が
範囲内の部分集合を 1 回の呼び出しで計算し、Is Nothing の分岐が「選択が領域を丸ごと外した」を
処理します。
Intersect と Union:重なり vs まとめる
Intersect(∩)と Union(∪)は、範囲に対する 2 つの集合演算子で、
正反対の方向へ引きます。Union(A, B) は、どちらかの範囲にあるすべてへ参照を大きくします。
Intersect(A, B) は、両方にあるものだけ、または Nothing へ小さくします。Union は決して
Nothing を返しません(組み合わせる実在の範囲を渡したのですから)。Intersect はしょっちゅう
Nothing を返し、それは欠陥ではなく持ち味です。散らばったセルをバッチ操作のために組み立てる
には Union、編集・選択・セルが気にかける領域の中に入るかを判定したり制限したりするには
Intersect を使いましょう。
複数範囲の Intersect と、同じシートのルール
Intersect は 2 つより多くの範囲を受け取り、それらすべてに共通するセルを返します —
Intersect(rngA, rngB, rngC) は、どの引数も覆う領域です。Union と同じく、すべての範囲は
同じワークシート上になければならず、さもないと error 1004 になります。そして同じ流儀が
結果にも当てはまります。重なりが空になりうるなら、使う前に Is Nothing を判定しましょう —
3 つ以上の範囲では、空の重なりは 2 つのときよりもさらに起こりやすいのです。
ExcelMaster の活用
Intersect は小さなメソッドですが、忘れやすい 2 つのルールを隠しています。結果に触れる前に
Is Nothing を確認すること、そしてイベントのガードを EnableEvents = False と組にして、自分で
引き起こした変更が暴走しないようにすること。前者を忘れれば error 91 でクラッシュし、後者を
忘れれば Worksheet_Change ハンドラーが自分自身を再発火させ、ちらつきやハングになります。
ExcelMaster なら、代わりに
振る舞いを述べるだけで済みます。「誰かが B 列を編集したら、C 列に時刻を刻んで」と言えば、
Intersect(Target, ...) のガード、早期の Exit Sub、EnableEvents の切り替え、CleanExit の
復元を書いてくれます — ハッピーパスだけでなく、堅牢なイベントパターン全体です。ブックも
コードもあなたの手元に残り、ハンドラーが間違ったセルで発火した、あるいは自分自身をループした、
あのデバッグを飛ばせます。
よくある質問
Excel VBA の Intersect メソッドは何をしますか?
Intersect は、2 つ以上の範囲が共通に持つセル — その重なり — の範囲を返します。
Intersect(Range("A1:D10"), Range("C5:F20")) は C5:D10 を返します。範囲がまったく重ならなければ
Intersect は Nothing を返し、だからこそ、ある範囲が別の範囲の中に入るかを判定するのに
使われます。
なぜ Intersect は Nothing を返してコードをクラッシュさせるのですか?
範囲がセルを 1 つも共有しないとき、Intersect は空の範囲ではなく Nothing を返します。その
あと、それに対するどんなプロパティ — .Address、.Cells、.Value — へ触れても error 91 が
発生します。使う前に必ず If Not Intersect(a, b) Is Nothing Then(「重なりがあるなら」の意味)で
先に判定しましょう。
Worksheet_Change イベントで Intersect をどう使いますか?
ハンドラーをガードして、対象範囲の中の編集にだけ反応させます。
If Intersect(Target, Me.Range("B2:B1000")) Is Nothing Then Exit Sub です。それから、どのセルへ
書き込む前にも Application.EnableEvents = False を設定し — さもないと書き込みが Worksheet_Change
を再び引き起こします — CleanExit ラベルの中で True に戻して、エラーがイベントを無効のまま
残せないようにします。
VBA で Intersect と Union の違いは何ですか?
正反対の集合演算です。Intersect は両方の範囲にあるセルだけ(重なり)、触れ合わなければ
Nothing を返します。Union はどちらかの範囲にあるセル(合わさった領域)を返します。
ある領域を判定したり制限したりするには Intersect、散らばったセルを 1 つの参照に組み立てるには
Union を使いましょう。
Intersect は 2 つより多くの範囲を取れますか?
はい。Intersect(rngA, rngB, rngC) は、範囲すべてに共通するセルを返します。すべての範囲は
同じワークシート上になければならず、範囲が共通のセルを共有しなければ結果は Nothing になりえます
— これは 3 つ以上の範囲でより起こりやすいので、必ず Is Nothing を確認しましょう。
検証環境
検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-16。
関連ガイド: VBA Union · VBA SpecialCells · VBA Worksheet_Change · VBA On Error · VBA Range
