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

Excel VBA の名前付き範囲 — Names.Add と RefersTo、そして参照がずれる理由

|

Excel VBA の名前付き範囲 — Names.Add と RefersTo、そして参照がずれる理由

TL;DR — 名前付き範囲は 保存された数式 であり、ラベルではありません。Range("B2:B50").Name = "SalesData" は 1 行で作る方法、Names.Add Name:="TaxRate", RefersTo:="=Sheet1!$B$1" は長い書き方です。誰もが つまずく 2 点:RefersTo は先頭の = 絶対参照の $ が必要で($ を省くと、名前は使うたびに 別のセルへ静かに ずれ ます)、名前には スコープ があります — ブック全体か 1 枚のシートか。名前は Range("TaxRate").Value[TaxRate] の短縮形で読みます。

' 1 行版:ブックスコープ、絶対参照 — よくあるケース
Range("B2:B50").Name = "SalesData"

' 長い書き方:スコープと RefersTo 数式を明示する
ThisWorkbook.Names.Add Name:="TaxRate", RefersTo:="=Config!$B$1"

Debug.Print Range("TaxRate").Value          ' 名前が指す値を読む
Range("SalesData").Interior.Color = vbYellow ' 名前をふつうの Range のように使う

誰もが、マクロのあちこちに Range("$B$2") を散らばせるのをやめようと名前付き範囲に手を伸ばし、そして小さな謎の連続にぶつかります。名前が間違ったセルを指す、あるいはリテラルのテキストを指す、あるいは同じ名前が 2 枚のシートで 2 つの異なるものを意味する、あるいは削除された列を名前が生き延びて、それを使うすべての数式を汚染する。これらはすべて、握っておく価値のある 1 つの考えから来ています。名前は保存された RefersTo 数式であり、Excel は使うたびにそれを再評価します — だから数式を支配するルール(先頭の =、絶対参照の $、シート修飾子)が、そのまま名前を支配するルールなのです。

この記事で学べること

  • 考え方の軸 — 名前は別名ではなく、保存された数式
  • 名前の作成 — 1 行の Range.Name と、RefersTo を伴う Names.Add
  • $ が、安定した名前とアクティブセルとともにずれる名前を分ける理由
  • スコープ — ブック全体か 1 枚のシートか、そしてそれが招く衝突
  • 名前の読み取りと使用、そしてセルを持たない定数の名前
  • #REF! の名前と、それが残す隠れた名前の肥大化の掃除

考え方の軸:名前は保存された数式

名前付き範囲を作るとき、Excel はセルに印を付けるのではありません。RefersTo 数式 を保存し、名前が現れるたびにそれを解決します。SalesData は「B2:B50 というセル」がその場に凍結されたものではなく、要求に応じて評価される数式 =Sheet1!$B$2:$B$50 です。Range("SalesData") を読めば、Excel はその数式を実行し、いま 解決される先が何であれ、それをあなたに手渡します。

だから、以下のすべては実のところ、RefersTo 数式を正しく書くことに尽きます。

ThisWorkbook.Names.Add Name:="SalesData", RefersTo:="=Sheet1!$B$2:$B$50"
Debug.Print ThisWorkbook.Names("SalesData").RefersTo   ' =Sheet1!$B$2:$B$50

RefersTo= で始まらなければならない文字列 で、名前の管理(Name Manager)に打ち込むのとまったく同じです。= を省く — RefersTo:="Sheet1!$B$2" — とエラーは出ず、リテラルの テキスト「Sheet1!$B$2」を指す名前ができてしまい、これはまず意図したものではありません。「これは数式だ」と握っておけば、= はもう謎ではなくなります。

名前の作成:1 行版と長い書き方

よくあるケース — 絶対参照でブックスコープの名前 — なら、範囲に .Name を代入すれば完了です。

Range("B2:B50").Name = "SalesData"      ' ブックスコープ、絶対参照、1 行

スコープを明示的に設定したいとき、数式で参照先を指定したいとき、あるいはセルではなく定数を格納したいときは、Names.Add に手を伸ばします。

ThisWorkbook.Names.Add Name:="TaxRate", RefersTo:="=Config!$B$1"   ' ブックスコープ
Worksheets("Jan").Names.Add Name:="Region", RefersTo:="=Jan!$A$1:$A$9" ' シートスコープ
ThisWorkbook.Names.Add Name:="VAT", RefersTo:="=0.2"              ' 定数、セルなし

どちらも問題ありません。違いは制御の度合いです。素の範囲には Range.Name = が最速で正しい方法、スコープや範囲以外の対象が問題になった瞬間に使うのが Names.Add です。

$ が、安定した名前とずれる名前を分ける理由

これは、人がうまく説明できない名前付き範囲のバグです。「マクロを走らせるたびに名前が別のセルを指す」というものです。原因は 相対 参照の名前 — $ のない RefersTo — です。

' ずれる — 相対参照、アクティブセルを基準に解決される
ThisWorkbook.Names.Add Name:="Prev", RefersTo:="=Sheet1!A1"

' 安定 — 絶対参照、つねに同じセル
ThisWorkbook.Names.Add Name:="Anchor", RefersTo:="=Sheet1!$A$1"

相対参照の名前は、作成時にアクティブセルがあった場所を基準として 保存され、Excel は使うたびにそれをアクティブセルへ結び直します — だから Prev は選択に応じて A1、次に D5、次に Z99 と解決されえます。相対参照の名前は、実在する、ときに役立つ機能(「1 つ左のセル」を意味する名前)ですが、意図してそうしたのでなければ、まるで幽霊のように見えます。名前を動かしたいと特に望むのでない限り、列と行の両方に $ を書きましょうRange.Name = を使えば自動的に絶対参照になります — これがそれをより安全な既定にするもう 1 つの理由です。

スコープ:ブック全体か 1 枚のシートか

どの名前も スコープ の中に生きています。ThisWorkbook.Names.Add(および Range.Name =)は、どこからでも見える ブックスコープ の名前を作ります。Worksheets("Jan").Names.Add は、そのシートでだけ見える ワークシートスコープ の名前を作ります — これにより JanFeb がそれぞれ自分のデータを指す独自の Region という名前を持てます。

罠は、両者を取り違えることです。

Worksheets("Jan").Names.Add Name:="Region", RefersTo:="=Jan!$A$1:$A$9"
' 標準モジュールからは、シートローカルの名前を確実には見られない:
' Debug.Print Range("Region").Address   ' エラーになるか、別の Region に当たる
Debug.Print Worksheets("Jan").Range("Region").Address   ' シートで修飾する -> 動く

シートスコープの名前は、モジュールから素の Range("Region") で呼ぶのではなく、そのシートを通してWorksheets("Jan").Range("Region") — 到達しなければなりません。意図して決めましょう。ファイル全体で 1 つの定数(TaxRate)はブックスコープ、シートごとに繰り返す領域(各月タブの Region)はシートスコープで、毎回修飾します。シートの指定方法は VBA Worksheet を参照してください。

読み取り、使用、そして定数の名前の落とし穴

範囲の名前は、ふつうの Range と同じように振る舞います。読み、書き、書式を設定できます。

Range("TaxRate").Value = 0.19            ' 名前付きのセルに書き込む
Debug.Print Range("SalesData").Cells.Count
Set rng = ThisWorkbook.Names("SalesData").RefersToRange  ' Range オブジェクト

Range("TaxRate").Value は値を読み、[TaxRate] は同じことの短縮形です(実体は Evaluate("TaxRate"))。ただし上の最後の行に注意してください。.RefersToRange は、名前が セル を指すときにだけ動きます。定数 を格納した場合 — Names.Add Name:="VAT", RefersTo:="=0.2" — その背後に範囲は存在しないので、Range("VAT").RefersToRange はエラーになり、一方 [VAT]Evaluate("VAT") は正しく 0.2 を返します。セルとして扱う前に、自分がどちらの種類の名前を持っているかを知りましょう。名前が数式としてどう解決されるかは VBA Formula を参照してください。

#REF! の名前と隠れた名前の肥大化を掃除する

名前がカバーする行や列を削除しても、名前は 死にません — その RefersTo=#REF! になり、その名前を参照するすべての数式を壊す、生きた地雷になります。名前はシートをコピーするときにも一緒に移動し、静かに蓄積して、やがてブックは数千もの隠れた壊れた名前を抱え、這うように遅くなります。どちらも、名前の点検が一度きりの設定ではなく保守作業である理由です。

Dim nm As Name
For Each nm In ThisWorkbook.Names
    If InStr(1, nm.RefersTo, "#REF!") > 0 Then
        Debug.Print "broken: " & nm.Name & " -> " & nm.RefersTo
        nm.Delete                      ' 地雷を取り除く
    End If
Next nm

ThisWorkbook.Names をループし、RefersTo#REF! を含むものに印を付け、Delete します。同じループの条件を nm.Visible = False にして走らせれば、貼り付けたシートが引きずり込んだ隠れた名前を見つけられます。名前は作るのが安価で、忘れるのが簡単です — 名前のリストは、ただ埋めるものではなく、掃除するものとして扱いましょう。

ExcelMaster の活用

本当に時間を奪う名前付き範囲のミスは、タイプミスではありません — $ が抜けてずれる相対参照の名前、モジュールから見えないシートスコープの名前、定数の名前で吹き飛ぶ .RefersToRange、そして帳票が間違って出るまで誰も気づかなかった #REF! の名前です。どれも走りはします。ただ、間違った場所へ解決されるだけです。

ExcelMaster は、慎重な開発者がやるように名前を書きます。「税率のセルに名前を付けて、計算で使って」と頼めば、絶対参照でブックスコープの名前を作り、$B$1 をハードコードする代わりにどこでもそれを参照し、領域が本当にタブごとに繰り返すときだけシートスコープを選びます。範囲の名前と定数の名前で正しい呼び出しを使い分けて名前付きの値を読み、ブックが溜め込んだ壊れた #REF! の名前を点検して消せます。あなたは意味するものに名前を付けるだけ。行が挿入されても静かに壊れないよう参照を配線するのは、それがやります。

よくある質問

VBA で名前付き範囲を作成するには?

最も短いのは、範囲の Name プロパティに代入する方法です。Range("B2:B50").Name = "SalesData" は、ブックスコープで絶対参照の名前を作ります。より細かく制御したいときは Names.Add を使います。ThisWorkbook.Names.Add Name:="TaxRate", RefersTo:="=Config!$B$1" です。RefersTo の文字列は = で始まらなければならず、名前がずれないよう、ほぼつねに絶対参照の $ がほしいところです。

名前のブックスコープとワークシートスコープの違いは?

ブックスコープの名前(ThisWorkbook.Names.Add または Range.Name =)は、すべてのシートとすべてのモジュールから見えます。ワークシートスコープの名前(Worksheets("Jan").Names.Add)はそのシートでだけ見えるので、異なるシートが同じ名前を自分のデータに再利用できます。シートスコープの名前は、素の Range("Region") ではなく、そのシートを通して — Worksheets("Jan").Range("Region") — 到達します。

VBA で名前が指す範囲を取得するには?

Range オブジェクトを得るには ThisWorkbook.Names("SalesData").RefersToRange を使い、ブックスコープの名前なら単に Range("SalesData") でも取れます。値を読むには Range("SalesData").Value[SalesData] の短縮形です。.RefersToRange はセルを指す名前でだけ動きます — =0.2 のような定数の名前には範囲がないので、Evaluate[Name] で読まなければなりません。

VBA で名前付き範囲が #REF! を指すのはなぜ?

参照していたセルが削除されたからです。名前がカバーする行や列を削除しても名前は削除されず、代わりにその RefersTo=#REF! になり、その名前を使うすべての数式が壊れます。ThisWorkbook.Names をループして nm.RefersTo#REF! が含まれるかを確認し、壊れたものに nm.Delete を呼んで点検しましょう。

VBA で名前付き範囲を削除するには?

名前に .Delete を呼びます。ThisWorkbook.Names("SalesData").Delete です。あるいは ThisWorkbook.Names をループして条件で削除します(たとえば RefersTo#REF! を含むもの)。名前を削除してもセルには触れません。定義された名前を取り除くだけで、これがシートのコピーで溜まる壊れた名前や隠れた名前を掃除する方法です。

検証環境

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

関連ガイド: VBA Range · VBA Cell Value · VBA Formula · VBA Worksheet · VBA Offset