C#における配列は、同じ型のデータを連続したメモリ領域にまとめて管理するための最も基本的なデータ構造です。

プログラミングにおいて、複数の値を効率的に扱うためには配列の理解が欠かせません。

現代のC#では、初期のバージョンから存在する伝統的な書き方に加え、C# 12で導入された「コレクション式」などの非常に簡潔で強力な記法が利用可能になっています。

本記事では、C#における配列の宣言から初期化、そして最新の構文まで、開発現場で即戦力となる知識を詳しく解説します。

配列の基本概念と宣言の仕組み

C#の配列は「参照型」に分類されます。

配列変数を宣言しただけでは、実体(要素を格納するメモリ領域)は作成されず、単に「配列を指し示すための変数」が準備された状態になります。

配列の宣言

配列を宣言するには、型名の後ろに [] を付けます。

C#
// 整数型の配列を宣言する
int[] numbers;

// 文字列型の配列を宣言する
string[] names;

この時点では、変数 numbersnames の中身は null であり、要素にアクセスしようとすると例外が発生します。

配列を使用するには、次に説明する「初期化」というプロセスが必要です。

配列の生成(インスタンス化)

配列の実体を作成するには、new 演算子を使用してサイズを指定します。

C#
// 5つの要素を持つ配列を生成
int[] numbers = new int[5];

この操作により、メモリ上のヒープ領域に5つの整数を格納する領域が確保されます。

C#の配列は、生成時にすべての要素が型に応じた既定値(デフォルト値)で自動的に初期化されるという特徴があります。

既定値
数値型(int, double等)0
bool型false
参照型(string, クラス等)null
char型‘\0’ (ヌル文字)

配列を初期化するさまざまな方法

実務では、宣言と同時に具体的な値を代入したい場面が多くあります。

C#には、状況に応じて使い分けられる複数の初期化方法が用意されています。

1. new演算子を用いた標準的な初期化

最も明示的な方法は、new と波括弧 {} を組み合わせる方法です。

C#
// サイズを指定して初期値を代入
int[] numbers = new int[3] { 10, 20, 30 };

// サイズを省略(コンパイラが要素数から自動判断)
string[] colors = new string[] { "Red", "Green", "Blue" };

2. 暗黙的な型指定による初期化

C# 3.0以降では、var キーワードを使用して型推論を利用できます。

C#
// コンパイラが右辺から型を推論する
var scores = new[] { 90, 80, 75, 100 };

この場合、要素の型がすべて一致している必要がある点に注意してください。

3. C# 12以降の最新記法:コレクション式

C# 12から導入された「コレクション式」は、現在推奨される最もモダンな書き方です。

角括弧 [] だけで配列やリストを初期化できます。

C#
// 非常にシンプルに記述可能
int[] modernArray = [1, 2, 3, 4, 5];

// 空の配列も直感的
string[] emptyArray = [];

従来の new int[] { ... } よりも記述量が少なく、可読性が大幅に向上しています。

また、この記法は後述する「スプレッド演算子」などの強力な機能もサポートしています。

多次元配列とジャグ配列(配列の配列)

C#には、表形式のデータを扱う「多次元配列」と、配列の中に配列を持たせる「ジャグ配列」の2種類が存在します。

これらはメモリ構造が異なるため、用途に応じた使い分けが重要です。

多次元配列(矩形配列)

多次元配列は、すべての行の長さが等しい配列です。

宣言にはカンマ , を使用します。

C#
// 2行3列の多次元配列
int[,] matrix = new int[2, 3] {
    { 1, 2, 3 },
    { 4, 5, 6 }
};

// 要素へのアクセス
int value = matrix[0, 1]; // 2

ジャグ配列

ジャグ配列は「配列の配列」であり、各行の長さが異なっていても構いません。

C#
// ジャグ配列の宣言
int[][] jagged = new int[3][];

// 各要素(行)を個別に初期化
jagged[0] = [1, 2];
jagged[1] = [3, 4, 5, 6];
jagged[2] = [7];

// 要素へのアクセス
int item = jagged[1][2]; // 5

パフォーマンス面では、ジャグ配列の方が多次元配列よりも高速に動作する傾向があります。

これは、.NETランタイムが1次元配列のアクセスに対して高度な最適化(境界チェックの省略など)を行うためです。

配列の基本操作とプロパティ

配列を宣言・初期化した後は、値の取得や変更、情報の取得などの操作を行います。

要素の取得と変更

配列の要素には「インデックス(添え字)」を使用してアクセスします。

インデックスは 0から始まる ことに注意してください。

C#
string[] fruits = ["Apple", "Banana", "Cherry"];

// 値の変更
fruits[1] = "Blueberry";

// 値の取得
string firstFruit = fruits[0];

配列のサイズを取得する

配列の要素数を知るには Length プロパティを使用します。

C#
int[] data = [10, 20, 30, 40];
Console.WriteLine(data.Length); // 出力: 4

多次元配列の場合、全体の要素数は Length、特定の次元の長さは GetLength(次元) メソッドを使用します。

配列の反復処理(ループ)

配列内のすべての要素に対して処理を行う場合、主に for 文または foreach 文を使用します。

foreach文による反復

要素を読み取るだけであれば、foreach が最も安全で簡潔です。

C#
int[] numbers = [1, 2, 3, 4, 5];

foreach (int n in numbers)
{
    Console.WriteLine(n);
}

for文による反復

インデックスを直接操作したい場合や、要素の値を書き換えたい場合は for 文を使用します。

C#
int[] values = [1, 2, 3];

for (int i = 0; i < values.Length; i++)
{
    values[i] *= 2; // 各要素を2倍にする
}

System.Arrayクラスの活用

C#の配列はすべて System.Array クラスを継承しています。

このクラスが提供する静的メソッドを利用することで、配列のソートや検索を簡単に行うことができます。

ソート(並べ替え)と反転

C#
using System;

int[] scores = [80, 50, 95, 30];

// 昇順にソート
Array.Sort(scores);

// 配列を反転
Array.Reverse(scores);

foreach (var s in scores)
{
    Console.Write($"{s} ");
}
実行結果
95 80 50 30

検索とコピー

Array.IndexOf で特定の要素の位置を検索したり、Array.Copy で配列の一部を別の配列へコピーしたりすることが可能です。

C#
string[] members = ["Alice", "Bob", "Charlie"];

// "Bob"のインデックスを検索
int index = Array.IndexOf(members, "Bob"); // 1

// 配列のコピー
string[] target = new string[3];
Array.Copy(members, target, members.Length);

配列の最新機能:スプレッド演算子と範囲(Range)

C# 8.0以降、そしてC# 12のアップデートにより、配列の操作はさらに柔軟になりました。

範囲指定(インデックスとレンジ)

^ 演算子(末尾からのインデックス)と .. 演算子(範囲)を使うことで、配列の一部を簡単に切り出すことができます。

C#
int[] original = [0, 1, 2, 3, 4, 5];

// 末尾から1番目(5)を取得
int last = original[^1];

// インデックス1から4の前まで(1, 2, 3)を取得
int[] subArray = original[1..4];

スプレッド演算子による結合

C# 12のコレクション式では、.. を使って既存の配列を別の配列に展開(結合)できます。

C#
int[] part1 = [1, 2];
int[] part2 = [4, 5];

// 既存の配列を組み込んで新しい配列を作成
int[] combined = [..part1, 3, ..part2];

// 実行結果: [1, 2, 3, 4, 5]

この機能により、複数の配列をマージするコードが圧倒的にシンプルになりました。

配列を使用する際の注意点とベストプラクティス

配列は強力ですが、特性を理解せずに使用するとバグやパフォーマンス低下の原因になります。

配列のサイズは固定

配列は一度生成するとそのサイズを変更することができません。

要素を追加・削除したい場合は、新しいサイズの配列を再作成してデータをコピーする必要があります。

動的なサイズ変更が頻繁に発生する場合は、List<T> クラスの使用を検討してください。

境界外アクセスの防止

配列の範囲外(例:サイズが5なのにインデックス5にアクセスする)を指定すると、IndexOutOfRangeException が発生します。

ループ処理では常に Length プロパティを基準にすることを徹底しましょう。

パフォーマンスを意識したSpan<T>の活用

大量のデータを扱う際、配列の一部をコピーして渡すとメモリ消費(アロケーション)が激しくなります。

モダンなC#開発では、メモリをコピーせずに配列の一部を「参照」として扱う Span<T> や ReadOnlySpan<T> を活用することで、極めて高いパフォーマンスを実現できます。

C#
int[] bigData = new int[10000];
// 配列の一部をコピーなしで参照する
Span<int> slice = bigData.AsSpan(100, 500);

まとめ

C#の配列は、歴史のある言語機能でありながら、現在も進化を続けています。

  • 基本的な 宣言・初期化new 演算子を用いる。
  • C# 12以降 は、より簡潔な コレクション式 [] を積極的に活用する。
  • 配列のサイズは 固定 であり、要素数は Length で取得する。
  • データの柔軟な操作が必要なら ジャグ配列LINQスプレッド演算子 を併用する。
  • パフォーマンスが重要な場面では Span<T> の利用を検討する。

これらの手法を適切に使い分けることで、バグが少なく、かつメンテナンス性の高いコードを記述できるようになります。

まずは基本のコレクション式をマスターし、徐々に高度な操作へとステップアップしていきましょう。