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

Excel VBA のセルの値 — .Value の読み書きと Range を配列にするテクニック

|

Excel VBA のセルの値 — .Value の読み書きと Range を配列にするテクニック

TL;DRRange("A1").Value は 1 つのセルを読み書きします。重要なのは 複数 セルでの挙動 です。arr = Range("A1:C1000").Value は、ブロック全体を 1 始まりの 2 次元 Variant 配列 として 1 回の呼び出しで読み込み、Range("A1:C1000").Value = arr は 1 回の呼び出しで書き戻します。 Cells(i, j).Value をループで 1 セルずつ触れると、読み込みのたびに VBA から Excel へ境界を 越えます。演算ではなく、その境界越えこそが、大きなループを遅くする正体です。

Dim v As Variant
v = Range("A1").Value              ' 1 つのセルを変数に読み込む
Range("B1").Value = v * 1.08       ' 1 つのセルに書き込む

Dim arr As Variant
arr = Range("A1:A10000").Value     ' 10,000 セルを 1 回の呼び出しで読む -> 2 次元配列
' ... arr をメモリ上で処理する ...
Range("C1:C10000").Value = arr     ' すべてを 1 回の呼び出しで書き戻す

値の読み書きは、VBA で誰もが最初にやることであり、単一セルなら見た目どおりに単純です。本記事が 拠って立つ考え方は、セルが 2 つ以上になった瞬間に姿を現します。範囲に対する .Value は 1 つの 値ではなく、ブロック — 2 次元配列 — であり、そのブロックを 1 セルずつではなく 1 回の呼び出しで 動かすことが、一瞬で終わるように感じるマクロと固まってしまうマクロの分かれ目になります。 これ さえ押さえれば、速度のルールも、小さな型の罠も、すべて腑に落ちます。

この記事で学べること

  • 考え方の軸 — .Value はブロックであり、範囲は 2 次元 Variant 配列を返す
  • 最も大事なルール — セル単位ではなく、ブロック全体を 1 往復で読み書きする
  • 配列が 1 始まりで 2 次元 になる理由 — 1 列だけでも
  • 単一セルの特例 — 1 セルはスカラーであって配列ではない
  • .Value は既定プロパティ — そのショートカットと、頼るべきでない理由
  • 値を代入すると、数式も含めてセルの中身が置き換わる

考え方の軸:.Value はブロック

1 つのセルなら、.Value は読み書きする単一の値です。

Dim name As String
name = Range("A1").Value           ' スカラーを読む
Range("A2").Value = "Total"        ' スカラーを書く

多数のセルからなる範囲では、.Valueグリッド全体を一度に 表します。Variant に読み込めば 2 次元配列が得られ、2 次元配列を代入すればすべてのセルを 1 手で設定できます。

Dim block As Variant
block = Range("A1:C100").Value     ' block は 100 x 3 の Variant 配列になる

大事な言葉は 一度 です。コードが .Value を読み書きするたびに、VBA エンジンと Excel アプリケーションの境界 — 比較的コストの高い一跳び — を越えます。1 つのセルは一跳び、ブロック全体を 配列に読み込むのも やはり 一跳びです。この一点 — 範囲は 1 回の境界越えで動かせるブロックである — が、以下の性能ルールを動かしており、.Value について知っておくべき最も価値のある事実です。

最も大事なルール:配列を往復させ、セルをループしない

同じ仕事を 2 通りでやってみましょう。ある列を合計し、各値を 8% 増しにして隣に書き込みます。

遅いやり方は、反復のたびに Excel に触れます。

Dim i As Long
For i = 1 To 10000
    Cells(i, 3).Value = Cells(i, 1).Value * 1.08   ' 1 行あたり 2 回の境界越え = 20,000 跳び
Next i

速いやり方は合計 2 回だけ越え、作業をメモリ上で行います。

Dim src As Variant, out() As Variant, i As Long
src = Range("A1:A10000").Value          ' 読み込みで 1 跳び
ReDim out(1 To UBound(src, 1), 1 To 1)
For i = 1 To UBound(src, 1)
    out(i, 1) = src(i, 1) * 1.08        ' 純粋な VBA — Excel は関与しない
Next i
Range("C1:C10000").Value = out          ' 書き込みで 1 跳び

演算は同じ、ループの長さも同じです。唯一の違いは、2 番目の版が 10,000 セルを 1 回の呼び出しで読み、 10,000 セルを 1 回の呼び出しで書くのに対し、最初の版は VBA と Excel の境界を 20,000 回も別々に往復 することです。実データでは配列版がふつう数十倍速く — しばしば「一瞬」と「砂時計を眺める」の差に なります。さらに大きく稼ぐには、書き込みのあいだ再計算と画面更新をオフにすることと組み合わせ ましょう。VBA CalculationVBA ScreenUpdating を参照してください。コストは境界越えであり、配列の 往復こそが、それをセルごとに払うのをやめる方法です。

配列が 1 始まりで 2 次元になる理由

範囲から得られる Variant 配列は、人を 2 度驚かせます。1 つ目、それは Option Base の設定に関係なく つねに 1 始まり です — arr(1, 1) が左上のセルです。2 つ目、範囲が 1 列や 1 行であっても、それは つねに 2 次元 です。

Dim col As Variant
col = Range("A1:A5").Value          ' 1 列だけの範囲
Debug.Print col(3, 1)              ' col(3) ではない — (行, 列) である

1 列の範囲は 5 行 1 列の配列なので col(row, 1) で添字を付け、1 行の範囲は 1 行 5 列で row(1, col) です。col(3, 1) の代わりに col(3) と書くと「Subscript out of range」が発生し、配列に切り替えた ばかりの人が最もよく犯すミスです。1 列から本当に 1 次元配列がほしいなら、読み込みを Application.Transpose で包みます — ただし Transpose には独自の制約があり(要素数はおよそ 65,536 が 上限で、型を強制変換します)、大きなデータや混在データには素の 2 次元配列のほうが安全です。結果を 扱う方法は VBA Array を参照してください。

単一セルの特例

配列コードを壊す縁がひとつあります。範囲が 単一セル の場合、.Value は 1 行 1 列の配列 ではなく、 素のスカラーを返します。

Dim v As Variant
v = Range("A1").Value              ' A1 単独 -> スカラーであって v(1, 1) ではない
' ここで v(1, 1) はエラーになる

つまり、rng.Value を配列に読み込んでから arr(1, 1) で添字を付けるコードは、複数セルの rng では 動きますが、rng がたまたま 1 セルだと破綻します。範囲の大きさが 1 セルまで変わりうるなら、最小 サイズを強制するか、結果を配列として扱う前に rng.Cells.Count > 1 を確認しましょう。これは Excel の オブジェクトモデルの小さな不整合ですが、「大きなシートでは動くのに小さなシートで落ちる」という現実の バグを生みます。

.Value は既定プロパティ — ショートカットとそのリスク

.Value は Range の 既定プロパティ であり、Excel はそれを省略させてくれます。

x = Range("A1")                    ' 動く — 暗黙の .Value
Range("A1") = 42                   ' 動く — 暗黙の .Value

すっきり読め、多くのコードがそうしています。リスクは、そのショートカットが どの 操作を意図して いるかを隠すことです。Set rng2 = Range("A1")Range オブジェクト を代入し、x = Range("A1") はその を代入します — Set の有無が意味を静かに変えます。そして If Range("A1") = Range("B1") は値を比較しますが、コードが値について何も言っていないとき、読み手の期待どおりとは限りません。保つ 価値のある習慣はこれです。値を意図するなら .Value を明示的に書く。 わずか 6 文字のコストで、 「セルなのか中身なのか」という曖昧さのひと群れを — とりわけ Set まわりで — 取り除けます。詳しくは VBA Range を参照してください。

値の代入は数式を置き換える

値と数式を混ぜる人がつまずく、最後のひとつです。セルに .Value を書き込むと、数式も含めて そこに あったものが上書きされます

Range("D2").Formula = "=B2*C2"     ' D2 は今や生きた数式
Range("D2").Value = 100            ' D2 は今や静的な数値 100 — 数式は消えた

これはたいてい望みどおりの動作です — 単一セルで「数式をその結果に変換する」まさにそのやり方です (自分自身の上に値を代入し直す)。ただし、それが意味するのは、値を生んだ数式を壊さずに値だけを ちょっと動かすことはできない、ということです。セルに生きた数式が必要なときは、.Value ではなく .Formula を代入します — 両者は同じセルの異なる顔であり、VBA Formula で 扱っています。そして、読み戻す 正確な 数値が問題になるとき — お金、日付、長い小数 — には、.Value.Value2 の選択が効いてきます。それが VBA Value vs Value2 vs Text の テーマです。

ExcelMaster の活用

本当に時間を奪う .Value のミスは、タイプミスではありません — 大きなシートでの遅いセル単位の ループ、arr(3, 1) であるべき arr(3)、1 セルの範囲で壊れる配列コード、そして間違ったものを静かに 比較した暗黙の .Value です。どれも動きはします。ただ、間違って動くか、遅く動くだけです。

ExcelMaster は、既定で速く 正しい版を書きます。「A 列を 8% 増しにして C 列へ」と頼めば、ブロックを Variant 配列に読み込み、 メモリ上で処理し、1 回の代入で書き戻します — 書き込みの前後で再計算と画面更新も面倒を見ます。 2 次元配列に正しく添字を付け、単一セルの場合を守り、コードが意味どおりになるよう .Value を明示的に 書きます。あなたは変換を説明するだけ。這うようなループの代わりに、まばたきの間に走る往復を書くのは、 それがやります。

よくある質問

Excel VBA でセルの値を取得するには?

Range の Value プロパティを読みます。x = Range("A1").Value または x = Cells(1, 1).Value です。 多数のセルを一度に扱うなら、範囲全体を Variant に読み込みます — arr = Range("A1:C100").Value — と、 1 始まりの 2 次元配列(arr(row, column))が得られます。ブロックを配列に読み込むほうが、ループで各 セルを読むよりはるかに高速です。個々の .Value アクセスが VBA から Excel へ境界を越えるからです。

VBA でセルに値を書き込むには?

Value に代入します。Range("A1").Value = 42 または Cells(1, 1).Value = "Total" です。多数の セルを一度に書くには、2 次元の Variant 配列を作り、同じ形の範囲に代入します。 Range("A1:A100").Value = arr です。これで、ループの代わりに 100 セルすべてを 1 回の操作で書き込め、 大きな範囲では劇的に高速になります。

VBA の範囲の値が 2 次元配列になるのはなぜ?

複数セルの範囲はグリッドだからです。その .Valuearr(row, column) で添字を付ける 2 次元配列で、 つねに 1 始まりです — 1 列でも N 行 1 列の配列なので、arr(3) ではなく arr(3, 1) で添字を付けます。 添字を 1 つだけで指定すると「Subscript out of range」が発生します。単一セルは例外です。 Range("A1").Value は 1 行 1 列の配列ではなく、素のスカラーを返します。

VBA のループでセルを読むのがなぜ遅いのか?

Cells(i, j).Value のアクセスはどれも VBA エンジンと Excel アプリケーションの境界を越え、その一跳び こそがコストの高い部分です — 演算ではありません。10,000 行のループで各セルを読み書きすると 20,000 回の境界越えが発生します。範囲を配列に読み込み、メモリ上で処理し、配列を書き戻すと、境界越えは たった 2 回で済みます。だからしばしば数十倍速くなるのです。

VBA で .Value と、単なる Range("A1") の違いは?

.Value は Range の既定プロパティなので、x = Range("A1")x = Range("A1").Value として扱われ ます。値としては同じに読めますが、.Value を省くと意図が隠れます。Set rng = Range("A1") は Range オブジェクトを代入し、x = Range("A1") はその値を代入します。違いは Set キーワードの有無だけです。 .Value を明示的に書けば、コードは曖昧さのないものになります。

検証環境

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

関連ガイド: VBA Range · VBA Cells · VBA Formula · VBA Value vs Value2 vs Text · VBA Array