C#でExcelのRange操作を完全攻略|範囲指定・値取得・書き込み・結合セルまで解説

はじめに

C#でExcelを操作する場面では、セルや表の範囲を扱う「Range」の理解が欠かせません。単一セルの値取得、複数セルへの一括書き込み、表全体の読み込み、結合セルの扱いなど、Excel自動化の多くはRange操作を中心に組み立てます。

特に Microsoft.Office.Interop.Excel を使う場合、Excel VBAに近い感覚でRangeを扱えますが、C#ならではの型変換、COMオブジェクトの解放、ValueValue2 の違いなどに注意が必要です。

この記事では、C#でExcelのRangeを操作する基本から、範囲指定、値取得、値の書き込み、大量データを扱う際のコツまでを、実務で使いやすいコード例とともに解説します。

1. C#でExcelのRangeを操作する前に知っておくべき基礎

1-1. Rangeとは何か:Excel上のセル範囲を表すオブジェクト

ExcelのRangeとは、ワークシート上のセルまたはセル範囲を表すオブジェクトです。

たとえば、Excel VBAでは次のようにセル範囲を指定します。

C#
worksheet.Range["A1"];
worksheet.Range["A1:C10"];

C#でもExcel Interopを使う場合、同じようにRangeを扱えます。

Rangeで指定できる対象は、単一セルだけではありません。

C#
worksheet.Range["A1"];       // A1セル
worksheet.Range["A1:C10"]; // A1からC10まで
worksheet.Rows[1]; // 1行目
worksheet.Columns[1]; // A列
worksheet.UsedRange; // 使用済み範囲

つまりRangeは、C#からExcelを操作するときの「セル操作の基本単位」です。

値の取得や書き込みだけでなく、文字色、背景色、罫線、表示形式、結合セル、列幅、行の高さなどもRangeを通じて操作できます。

1-2. C#でExcelを操作する主な方法:Interop・ClosedXML・EPPlusの違い

C#でExcelファイルを操作する方法はいくつかあります。代表的なものは次の3つです。

まず、Microsoft.Office.Interop.Excel は、Excelアプリケーション本体をC#から操作する方法です。Excelがインストールされている環境で動作し、VBAに近い書き方でRangeを操作できます。既存のマクロ付きファイルや細かいExcel機能を扱いやすい一方、COMオブジェクトの解放やExcelプロセスの管理に注意が必要です。

次に、ClosedXML は、Excelファイルを直接読み書きできるライブラリです。Excel本体がインストールされていなくても .xlsx ファイルを操作できます。セル範囲の取得や値の読み書きも直感的で、帳票出力やデータ編集に向いています。

最後に、EPPlus もExcelファイルを直接操作できるライブラリです。大量データの処理やスタイル設定などに強く、サーバーサイドでExcelファイルを生成する用途でもよく使われます。ただし、利用条件やライセンスは事前に確認する必要があります。

この記事では、ExcelのRangeという概念を理解しやすいように、主に Microsoft.Office.Interop.Excel を使ったサンプルを中心に解説します。

1-3. この記事で扱うRange操作の前提とサンプル環境

この記事のサンプルでは、次のような環境を前提にします。

言語: C#
対象: Windowsアプリケーションまたはコンソールアプリケーション
Excel操作方法: Microsoft.Office.Interop.Excel
対象ファイル: .xlsx

C#からExcel Interopを使う場合、PCにMicrosoft Excelがインストールされている必要があります。

また、サンプルコードでは以下のようなExcelファイルを想定します。

A列: 商品名
B列: 数量
C列: 単価
D列: 金額

Range操作では、セル番地を文字列で指定する方法と、行番号・列番号で指定する方法の両方を使います。

C#
worksheet.Range["A1"];
worksheet.Cells[1, 1];

どちらもA1セルを表しますが、固定のセルを指定する場合は Range["A1"]、ループや動的な範囲指定では Cells[行番号, 列番号] が便利です。

1-4. Range操作でよく使う名前空間・参照設定・基本コード

Excel Interopを使うには、プロジェクトに Microsoft.Office.Interop.Excel を追加します。

NuGetから追加する場合は、次のパッケージを使用します。

Microsoft.Office.Interop.Excel

C#コードでは、次のように名前空間を指定します。

C#
using Excel = Microsoft.Office.Interop.Excel;

Excelファイルを開き、WorksheetとRangeを取得する基本コードは次のとおりです。

C#
using System;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

class Program
{
static void Main()
{
Excel.Application excelApp = null;
Excel.Workbook workbook = null;
Excel.Worksheet worksheet = null;
Excel.Range range = null;

try
{
excelApp = new Excel.Application();
excelApp.Visible = false;

workbook = excelApp.Workbooks.Open(@"C:\sample\test.xlsx");
worksheet = workbook.Worksheets[1];

range = worksheet.Range["A1"];

Console.WriteLine(range.Value2);
}
finally
{
if (workbook != null)
{
workbook.Close(false);
Marshal.ReleaseComObject(workbook);
}

if (worksheet != null)
{
Marshal.ReleaseComObject(worksheet);
}

if (range != null)
{
Marshal.ReleaseComObject(range);
}

if (excelApp != null)
{
excelApp.Quit();
Marshal.ReleaseComObject(excelApp);
}
}
}
}

Excel Interopでは、Excelアプリケーションを起動して操作するため、処理後に Quit()Marshal.ReleaseComObject() を使ってCOMオブジェクトを解放することが重要です。

解放処理が不十分だと、タスクマネージャーに EXCEL.EXE が残り続けることがあります。

2. C#でExcelのRangeを取得する基本パターン

2-1. 単一セルをRangeで指定する方法

単一セルを取得する最も基本的な方法は、Range["セル番地"] を使う書き方です。

C#
Excel.Range cell = worksheet.Range["A1"];

このコードでは、A1セルをRangeオブジェクトとして取得しています。

値を取得する場合は、次のように書きます。

C#
object value = worksheet.Range["A1"].Value2;
Console.WriteLine(value);

値を書き込む場合は、次のようにします。

C#
worksheet.Range["A1"].Value2 = "商品名";

Range["A1"] は見た目にもわかりやすいため、固定のセルを指定する処理に向いています。

たとえば、帳票のタイトルや固定ヘッダーに値を書き込む場合に便利です。

C#
worksheet.Range["A1"].Value2 = "売上レポート";
worksheet.Range["A2"].Value2 = "作成日";
worksheet.Range["B2"].Value2 = DateTime.Now.ToString("yyyy/MM/dd");

2-2. 複数セルをRangeで指定する方法

複数セルをまとめて指定する場合は、Range["開始セル:終了セル"] の形式を使います。

C#
Excel.Range range = worksheet.Range["A1:D10"];

このコードでは、A1からD10までの範囲を取得しています。

複数セルに同じ値を書き込むこともできます。

C#
worksheet.Range["A1:D1"].Value2 = "ヘッダー";

また、背景色やフォントなどの書式設定も、範囲全体に対して一括で適用できます。

C#
Excel.Range headerRange = worksheet.Range["A1:D1"];

headerRange.Font.Bold = true;
headerRange.Interior.Color = System.Drawing.Color.LightGray;

Rangeを使って複数セルをまとめて処理すると、1セルずつ操作するよりもコードが短くなり、処理速度も改善しやすくなります。

2-3. Cellsを使って行番号・列番号で指定する方法

Cells を使うと、行番号と列番号でセルを指定できます。

C#
Excel.Range cell = worksheet.Cells[1, 1];

これはA1セルを表します。

Excelでは、行番号と列番号は1始まりです。

Cells[1, 1] = A1
Cells[1, 2] = B1
Cells[2, 1] = A2
Cells[3, 4] = D3

ループ処理でセルを操作する場合は、Range["A1"] よりも Cells[row, column] のほうが扱いやすくなります。

C#
for (int row = 1; row <= 10; row++)
{
worksheet.Cells[row, 1].Value2 = row;
}

このコードでは、A1からA10までに1から10までの数値を書き込んでいます。

列番号を変数で扱えるため、データテーブルや配列の内容をExcelに出力する処理でもよく使います。

2-4. RangeとCellsを組み合わせて動的に範囲指定する方法

開始セルと終了セルを動的に指定したい場合は、RangeCells を組み合わせます。

C#
Excel.Range startCell = worksheet.Cells[1, 1];
Excel.Range endCell = worksheet.Cells[10, 4];

Excel.Range range = worksheet.Range[startCell, endCell];

このコードでは、A1からD10までの範囲を取得しています。

行数や列数が変わる表を扱う場合に便利です。

C#
int startRow = 1;
int startCol = 1;
int endRow = 100;
int endCol = 5;

Excel.Range dataRange = worksheet.Range[
worksheet.Cells[startRow, startCol],
worksheet.Cells[endRow, endCol]
];

たとえば、データ件数に応じてExcelへの書き込み範囲を変えたい場合は、次のように書けます。

C#
int rowCount = 50;
int colCount = 4;

Excel.Range outputRange = worksheet.Range[
worksheet.Cells[2, 1],
worksheet.Cells[rowCount + 1, colCount]
];

このように、C#でExcelのRangeを動的に扱う場合は、Cells で位置を指定し、Range で範囲化するのが基本パターンです。

2-5. UsedRangeでデータが入力されている範囲を取得する方法

UsedRange を使うと、ワークシート上で使用されている範囲を取得できます。

C#
Excel.Range usedRange = worksheet.UsedRange;

UsedRange は、データが入力されているセルや書式が設定されているセルを含む範囲を返します。

行数と列数を取得する場合は、次のように書きます。

C#
Excel.Range usedRange = worksheet.UsedRange;

int rowCount = usedRange.Rows.Count;
int colCount = usedRange.Columns.Count;

Console.WriteLine($"行数: {rowCount}");
Console.WriteLine($"列数: {colCount}");

表全体を読み込む場合は、UsedRange.Value2 を使うと効率的です。

C#
object[,] values = usedRange.Value2 as object[,];

for (int row = 1; row <= rowCount; row++)
{
for (int col = 1; col <= colCount; col++)
{
Console.WriteLine(values[row, col]);
}
}

ただし、UsedRange は過去に書式設定されたセルも含むことがあります。見た目には空白でも、Excel内部では使用済み範囲として認識される場合があるため、完全にデータ範囲だけを取得したい場合は注意が必要です。

2-6. CurrentRegionで表全体の範囲を取得する方法

CurrentRegion は、指定したセルを含む連続した表範囲を取得する機能です。

C#
Excel.Range tableRange = worksheet.Range["A1"].CurrentRegion;

A1セルを起点に、空白行・空白列で囲まれた範囲を表全体として取得します。

たとえば、A1:D20に表があり、周囲が空白であれば、CurrentRegion はA1:D20を返します。

C#
Excel.Range tableRange = worksheet.Range["A1"].CurrentRegion;

int rowCount = tableRange.Rows.Count;
int colCount = tableRange.Columns.Count;

Console.WriteLine($"表の行数: {rowCount}");
Console.WriteLine($"表の列数: {colCount}");

UsedRange はシート全体の使用済み範囲を取得するのに対し、CurrentRegion は特定の表だけを取得したいときに便利です。

C#
Excel.Range salesTable = worksheet.Range["A1"].CurrentRegion;
Excel.Range masterTable = worksheet.Range["G1"].CurrentRegion;

1つのシートに複数の表がある場合は、CurrentRegion を使うことで対象の表だけを扱いやすくなります。

3. C#でExcel Rangeの値を取得する方法

3-1. ValueとValue2の違い

C#でExcel Rangeの値を取得するときは、Value または Value2 を使います。

C#
object value1 = worksheet.Range["A1"].Value;
object value2 = worksheet.Range["A1"].Value2;

一般的には、Value2 を使うことが多いです。

Value はExcelの通貨型や日付型を考慮して値を返します。一方、Value2 は通貨型や日付型の特殊な変換を行わず、よりシンプルな値として取得します。

特に日付セルを Value2 で取得すると、Excel内部の日付シリアル値として返ることがあります。

C#
object rawValue = worksheet.Range["A1"].Value2;
Console.WriteLine(rawValue);

Excelの日付は内部的には数値で管理されているため、C#側で DateTime.FromOADate() を使って変換できます。

C#
double serialDate = Convert.ToDouble(worksheet.Range["A1"].Value2);
DateTime date = DateTime.FromOADate(serialDate);

実務では、値の取得や書き込みには Value2 を基本にし、日付や数値はC#側で明示的に変換するのがおすすめです。

3-2. 単一セルの値を取得する方法

単一セルの値を取得する場合は、Rangeの Value2 を読み取ります。

C#
Excel.Range cell = worksheet.Range["A1"];
object value = cell.Value2;

Console.WriteLine(value);

文字列として取得したい場合は、Convert.ToString() を使うと安全です。

C#
string text = Convert.ToString(worksheet.Range["A1"].Value2);

数値として取得したい場合は、Convert.ToDouble()Convert.ToInt32() を使います。

C#
object value = worksheet.Range["B2"].Value2;

if (value != null)
{
int quantity = Convert.ToInt32(value);
Console.WriteLine(quantity);
}

空白セルの場合、Value2null になることがあります。したがって、値を取得する前にnullチェックを入れると安全です。

C#
object value = worksheet.Range["A1"].Value2;

if (value == null)
{
Console.WriteLine("空白セルです");
}
else
{
Console.WriteLine(value.ToString());
}

3-3. 複数セルの値を二次元配列として取得する方法

複数セルのRangeから値を取得すると、二次元配列として受け取れます。

C#
Excel.Range range = worksheet.Range["A1:C5"];
object[,] values = range.Value2 as object[,];

取得した配列は1始まりのインデックスになります。

C#
for (int row = 1; row <= values.GetLength(0); row++)
{
for (int col = 1; col <= values.GetLength(1); col++)
{
Console.WriteLine(values[row, col]);
}
}

ただし、単一セルを指定した場合、Value2 は二次元配列ではなく単一の値として返ります。

C#
object value = worksheet.Range["A1"].Value2;

そのため、複数セルとして処理する前提であれば、必ず2セル以上のRangeを指定するか、単一セルの場合の分岐を用意しておくと安全です。

C#
Excel.Range range = worksheet.Range["A1:C5"];
object rawValue = range.Value2;

if (rawValue is object[,] values)
{
Console.WriteLine(values[1, 1]);
}
else
{
Console.WriteLine(rawValue);
}

C#でExcelの大量データを読み込む場合、1セルずつ Cells[row, col].Value2 を呼び出すよりも、Range全体を一括で二次元配列に読み込むほうが高速です。

3-4. 空白セル・nullを安全に扱う方法

Excelの空白セルをC#で読み込むと、null になることがあります。

そのため、次のように直接 ToString() を呼び出すと例外が発生する可能性があります。

C#
string text = worksheet.Range["A1"].Value2.ToString();

安全に扱うには、Convert.ToString() を使います。

C#
string text = Convert.ToString(worksheet.Range["A1"].Value2);

Convert.ToString(null) は空文字列を返すため、nullでも例外になりません。

空白かどうかを判定したい場合は、次のように書けます。

C#
object value = worksheet.Range["A1"].Value2;

if (value == null || string.IsNullOrWhiteSpace(Convert.ToString(value)))
{
Console.WriteLine("空白です");
}
else
{
Console.WriteLine(value);
}

複数セルを読み込んだ場合も、配列内の値がnullになることがあります。

C#
object[,] values = worksheet.Range["A1:C10"].Value2 as object[,];

for (int row = 1; row <= values.GetLength(0); row++)
{
for (int col = 1; col <= values.GetLength(1); col++)
{
string text = Convert.ToString(values[row, col]);

if (string.IsNullOrWhiteSpace(text))
{
continue;
}

Console.WriteLine(text);
}
}

Excelデータは、空白セル、数式の結果が空文字のセル、見た目だけ空白のセルなどが混在しやすいため、nullチェックと空文字チェックをセットで行うのが実務的です。

3-5. 数値・日付・文字列を型変換して取得する方法

Excel Rangeから取得した値は、基本的に object 型になります。そのため、C#側で目的の型に変換します。

文字列として取得する場合は、次のようにします。

C#
string productName = Convert.ToString(worksheet.Range["A2"].Value2);

数値として取得する場合は、nullチェックをしたうえで変換します。

C#
object quantityValue = worksheet.Range["B2"].Value2;

int quantity = 0;

if (quantityValue != null)
{
quantity = Convert.ToInt32(quantityValue);
}

小数を含む数値であれば、doubledecimal を使います。

C#
object priceValue = worksheet.Range["C2"].Value2;

decimal price = 0;

if (priceValue != null)
{
price = Convert.ToDecimal(priceValue);
}

日付を取得する場合、Value2 ではExcelの日付シリアル値として返ることがあります。

C#
object dateValue = worksheet.Range["A2"].Value2;

DateTime date;

if (dateValue != null)
{
date = DateTime.FromOADate(Convert.ToDouble(dateValue));
Console.WriteLine(date.ToString("yyyy/MM/dd"));
}

より安全に変換したい場合は、専用メソッドを作っておくと便利です。

C#
static string GetString(object value)
{
return Convert.ToString(value);
}

static int GetInt(object value)
{
if (value == null) return 0;
return Convert.ToInt32(value);
}

static DateTime? GetDate(object value)
{
if (value == null) return null;
return DateTime.FromOADate(Convert.ToDouble(value));
}

Excelのセルには、見た目は数値でも実体は文字列というケースがあります。型変換エラーを避けるため、必要に応じて TryParse を使うとより安全です。

C#
string text = Convert.ToString(worksheet.Range["B2"].Value2);

if (int.TryParse(text, out int result))
{
Console.WriteLine(result);
}
else
{
Console.WriteLine("数値に変換できません");
}

3-6. 大量データを高速に読み込むコツ

C#でExcelの大量データを扱う場合、1セルずつ読み込む処理は遅くなりがちです。

避けたい例は次のようなコードです。

C#
for (int row = 1; row <= 10000; row++)
{
for (int col = 1; col <= 10; col++)
{
object value = worksheet.Cells[row, col].Value2;
}
}

この書き方では、セルごとにExcelとの通信が発生するため、データ量が増えるほど処理時間が長くなります。

高速化したい場合は、Range全体を一括で取得します。

C#
Excel.Range range = worksheet.Range["A1:J10000"];
object[,] values = range.Value2 as object[,];

for (int row = 1; row <= values.GetLength(0); row++)
{
for (int col = 1; col <= values.GetLength(1); col++)
{
object value = values[row, col];
}
}

さらに、画面更新や警告表示を無効にすると、処理速度を改善できます。

C#
excelApp.ScreenUpdating = false;
excelApp.DisplayAlerts = false;
excelApp.EnableEvents = false;

処理後は元に戻します。

C#
excelApp.ScreenUpdating = true;
excelApp.DisplayAlerts = true;
excelApp.EnableEvents = true;

大量データを読み込むときの基本は、次の3つです。

1セルずつ読まない
Range.Value2でまとめて読む
C#側の配列で処理する

この考え方を押さえるだけで、C#のExcel Range操作は大きく高速化できます。

4. C#でExcel Rangeに値を書き込む方法

4-1. 単一セルに値を書き込む方法

単一セルに値を書き込む場合は、Rangeの Value2 に値を代入します。

C#
worksheet.Range["A1"].Value2 = "商品名";
worksheet.Range["B1"].Value2 = "数量";
worksheet.Range["C1"].Value2 = "単価";
worksheet.Range["D1"].Value2 = "金額";

数値や日付も書き込めます。

C#
worksheet.Range["A2"].Value2 = "ノートPC";
worksheet.Range["B2"].Value2 = 3;
worksheet.Range["C2"].Value2 = 120000;
worksheet.Range["D2"].Value2 = 360000;

日付を書き込む場合は、DateTime をそのまま代入できます。

C#
worksheet.Range["A4"].Value2 = DateTime.Now;

表示形式を指定したい場合は、NumberFormat を設定します。

C#
worksheet.Range["A4"].NumberFormat = "yyyy/mm/dd";
worksheet.Range["A4"].Value2 = DateTime.Now;

金額の表示形式を設定する場合は、次のようにします。

C#
worksheet.Range["C2:D2"].NumberFormat = "#,##0";

単一セルへの書き込みは簡単ですが、大量データを1セルずつ書き込むと処理が遅くなります。その場合は、二次元配列を使った一括書き込みを検討します。

4-2. 複数セルに同じ値を書き込む方法

複数セルに同じ値を書き込む場合は、Rangeを指定して Value2 に値を代入します。

C#
worksheet.Range["A1:D1"].Value2 = "見出し";

このコードでは、A1からD1までのすべてのセルに「見出し」と書き込まれます。

複数行・複数列に同じ値を入れることもできます。

C#
worksheet.Range["A1:C10"].Value2 = 0;

このコードでは、A1からC10までのすべてのセルに0が書き込まれます。

書式もRange全体に一括で設定できます。

C#
Excel.Range range = worksheet.Range["A1:D1"];

range.Value2 = "ヘッダー";
range.Font.Bold = true;
range.HorizontalAlignment = Excel.XlHAlign.xlHAlignCenter;

ただし、実務では複数セルすべてに同じ値を入れるよりも、表データを一括で書き込むケースのほうが多くあります。

その場合は、二次元配列を作成してRangeに代入します。

C#
object[,] data = new object[3, 4]
{
{ "商品名", "数量", "単価", "金額" },
{ "ノートPC", 3, 120000, 360000 },
{ "マウス", 10, 2500, 25000 }
};

Excel.Range range = worksheet.Range["A1:D3"];
range.Value2 = data;

C#の配列は0始まりですが、Excel Rangeに一括代入する場合はそのまま渡せます。

大量データを書き込む場合も、1セルずつ代入するより二次元配列をRangeにまとめて代入するほうが高速です。

C#
int rowCount = 1000;
int colCount = 4;

object[,] data = new object[rowCount, colCount];

for (int row = 0; row < rowCount; row++)
{
data[row, 0] = $"商品{row + 1}";
data[row, 1] = row + 1;
data[row, 2] = 1000;
data[row, 3] = (row + 1) * 1000;
}

Excel.Range outputRange = worksheet.Range[
worksheet.Cells[1, 1],
worksheet.Cells[rowCount, colCount]
];

outputRange.Value2 = data;

このように、C#でExcel Rangeに値を書き込むときは、少量なら単一セル指定、大量なら二次元配列による一括書き込みを使い分けるのが基本です。

まとめ

C#でExcelのRangeを操作できるようになると、セルの値取得、値の書き込み、表全体の読み込み、動的な範囲指定などを効率よく実装できます。

単一セルを扱う場合は Range["A1"]、行番号や列番号を使って動的に指定する場合は Cells[row, col]、複数セルをまとめて扱う場合は Range[startCell, endCell] を使うのが基本です。

値の取得では、Value よりも Value2 を使う場面が多く、空白セルは null として返ることがあるため、Convert.ToString() やnullチェックを組み合わせると安全です。

また、複数セルの値は二次元配列として取得できるため、大量データを扱う場合は1セルずつ読み書きするのではなく、Range全体をまとめて処理することが重要です。

C#でExcel Rangeを操作する際のポイントは、次のとおりです。

固定セルは Range["A1"] で指定する
動的なセル位置は Cells[row, col] で指定する
表全体は UsedRange や CurrentRegion で取得する
値の取得・書き込みは Value2 を基本にする
大量データは二次元配列で一括処理する
空白セルや型変換には注意する
InteropではCOMオブジェクトを確実に解放する

Range操作は、C#でExcel自動化を行ううえで最も重要な基礎です。範囲指定、値取得、書き込みの基本パターンを押さえておけば、帳票作成、データ集計、Excelファイルの自動生成など、さまざまな業務処理に応用できます。