C#において配列は、複数のデータを効率的に管理するための最も基本的なデータ構造の一つです。

開発の現場では、単にデータを格納するだけでなく、どのように初期化し、メモリを効率的に使用するかがパフォーマンスやコードの可読性に直結します。

近年のC#(特にC# 12以降)では、コレクション式と呼ばれる非常に簡潔で強力な構文が導入され、配列の初期化方法は劇的に進化しました。

本記事では、初心者の方が押さえておくべき基本から、実務で役立つ多次元配列、さらには最新のC# 12/13におけるモダンな書き方までを網羅的に解説します。

この記事を読むことで、状況に応じた最適な配列の初期化方法を選択できるようになり、よりクリーンで効率的なコードを書くための知識が身に付くでしょう。

C#における配列の基本概念と宣言方法

C#の配列は同じ型の要素を複数まとめて扱うための参照型のデータ構造です。

配列を使用する際は、まず要素の型と変数名を宣言し、その後に必要なメモリ領域を確保(インスタンス化)するという手順を踏みます。

最も基本的な宣言と初期化の構文は、new 演算子を使用する方法です。

C#
// 要素数5の整数型配列を宣言し、メモリを確保する
int[] numbers = new int[5];

// 各要素にはデフォルト値(intの場合は0)が自動的に代入される

配列の型とメモリの仕組み

C#において配列は「クラス」の一種であり、ヒープ領域にメモリが割り当てられます。

配列を宣言しただけでは実体が存在せず、new キーワードを使って初めてメモリ上にデータ領域が作られます。

配列のサイズは固定であり、一度作成した後に要素数を変更することはできません

サイズを変更したい場合は、新しいサイズの配列を作成して内容をコピーするか、List<T> などの動的コレクションを検討する必要があります。

配列の初期化:伝統的な書き方からモダンな書き方まで

配列の宣言と同時に値を代入する方法は、C#のバージョンアップとともに洗練されてきました。

ここでは、現在でも使われている主要な初期化方法を紹介します。

1. 明示的な初期化(new 演算子の使用)

古くから使われている最も基本的な方法です。

型名と要素数を明示し、波括弧 {} 内に初期値を記述します。

C#
// 基本的な書き方
int[] traditionalArray = new int[3] { 10, 20, 30 };

// 要素数を省略することも可能(コンパイラが自動推論する)
int[] implicitSizeArray = new int[] { 1, 2, 3, 4, 5 };

2. 簡略化された初期化

左辺から型が明らかな場合、右辺の型名を省略して記述できます。

C#
// 右辺の型名を省略
int[] simplifiedArray = { 100, 200, 300 };

この書き方は非常に簡潔ですが、var キーワードと組み合わせて使うことはできません。

var を使用する場合は、右辺に型情報が必要になるためです。

3. 【最新】コレクション式(C# 12以降)

C# 12で導入されたコレクション式は、現在のC#において最も推奨される書き方です。

角括弧 [] を使用して直感的に記述できます。

C#
// C# 12以降の最新の書き方
int[] modernArray = [1, 2, 3, 4, 5];

// 空の配列も非常にシンプルに書ける
int[] emptyArray = [];

このコレクション式の優れた点は、配列だけでなく List<T>Span<T> に対しても同じ構文が使えることです。

これにより、データ構造に依存しない統一感のあるコードを記述することが可能になりました。

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

コレクション式では、..(スプレッド演算子)を使用して、他の配列やコレクションを新しい配列に展開して含めることができます。

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

// 既存の配列を組み合わせて新しい配列を作成
int[] combined = [..part1, 3, ..part2];

foreach (var item in combined)
{
    Console.WriteLine(item);
}
実行結果
1
2
3
4
5

配列の初期値とデフォルト挙動

配列を初期化する際、特定の値を指定しなかった要素には、その型のデフォルト値が自動的に割り当てられます。

各型のデフォルト値一覧

C#の各データ型におけるデフォルト値は以下の通りです。

型の種類具体的な型デフォルト値
数値型int, double, float など0 または 0.0
論理型boolfalse
文字型char\0 (null文字)
参照型string, 自作クラスなどnull
構造体struct各フィールドがデフォルト値の状態

特定の値で一括初期化する方法

全ての要素を 0 以外の特定の値(例:すべて -1 にするなど)で初期化したい場合、ループ処理を行う以外にも効率的な方法があります。

Array.Fill メソッド

Array.Fill メソッドを使用すると、配列全体または一部を指定した値で素早く埋めることができます。

C#
int[] scores = new int[10];
// 配列全体を -1 で埋める
Array.Fill(scores, -1);

Console.WriteLine(string.Join(", ", scores));
実行結果
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1

LINQを使用した初期化

特定の規則に基づいた値を生成したい場合は、LINQの Enumerable.RepeatEnumerable.Range が便利です。

C#
using System.Linq;

// 同じ値を繰り返す
int[] repeated = Enumerable.Repeat(7, 5).ToArray(); // [7, 7, 7, 7, 7]

// 連続した数値を生成する
int[] sequence = Enumerable.Range(1, 5).ToArray(); // [1, 2, 3, 4, 5]

ただし、LINQは柔軟な反面、内部でオブジェクトの生成や反復処理が行われるため、パフォーマンスが極めて重要な場面では Array.Fill や単純な for ループの方が適している場合があります。

多次元配列とジャグ配列の初期化

C#には、行列のような構造を扱う「多次元配列」と、配列の中に配列を入れる「ジャグ配列(配列の配列)」の2種類があります。

多次元配列(Multidimensional Arrays)

多次元配列は、各次元の要素数が固定された矩形の構造を持ちます。

カンマ [,] を使用して宣言します。

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

// 特定の要素にアクセス(1行目2列目)
Console.WriteLine($"Value: {matrix[1, 2]}");
実行結果
Value: 6

ジャグ配列(Jagged Arrays)

ジャグ配列は、要素ごとに異なる長さの配列を保持できる構造です。

二重の角括弧 [][] を使用します。

C#
// ジャグ配列の宣言と初期化
int[][] jaggedArray = 
[
    [1, 2, 3],
    [4, 5],
    [6, 7, 8, 9]
];

// 2番目の配列の要素にアクセス
Console.WriteLine($"Jagged Value: {jaggedArray[1][1]}");
実行結果
Jagged Value: 5

多次元配列とジャグ配列の使い分けについては、メモリ構造の違いを理解することが重要です。

多次元配列は単一のメモリブロックとして確保されるため局所性に優れますが、ジャグ配列は各行が独立したオブジェクトであるため、行の入れ替えが高速であるといった特徴があります。

現在の.NETランタイムでは、ジャグ配列の方が最適化されやすく実行速度が速い傾向にあります。

実践的なテクニックと注意点

配列を扱う上で、パフォーマンスの向上やバグの防止に役立つ高度な知識をいくつか紹介します。

Span<T> を活用したメモリ効率の最適化

最新のC#開発では、配列のサブセット(部分)を扱う際に Span<T>ReadOnlySpan<T> を使用することが推奨されます。

これらはメモリのコピーを発生させずに配列の一部を参照できるため、非常に高速です。

C#
int[] data = [10, 20, 30, 40, 50];

// 配列の 1番目から3要素分を「参照」する(コピーは発生しない)
Span<int> slice = data.AsSpan(1, 3);

slice[0] = 99; // 元の配列 data[1] が書き換わる

Console.WriteLine(string.Join(", ", data));
実行結果
10, 99, 30, 40, 50

配列が空かどうかの判定

配列が空かどうかを確認する場合、従来は Length プロパティを確認していましたが、C#のパターンマッチングを使用するとより安全に記述できます。

C#
int[] myValues = [];

// パターンマッチングによる判定
if (myValues is [])
{
    Console.WriteLine("配列は空です。");
}

// ヌルチェックも含めた判定
if (myValues is null or [])
{
    Console.WriteLine("配列が null または空です。");
}

stackalloc によるスタック割り当て

パフォーマンスが極限まで求められるアルゴリズム(画像処理や暗号化など)では、ヒープではなくスタック領域に配列を確保する stackalloc を検討することがあります。

C#
// スタック上にメモリを確保(ガベージコレクションの対象外となるため高速)
Span<int> fastBuffer = stackalloc int[3] { 1, 2, 3 };

ただし、スタック領域はサイズが限られているため、大きなサイズの配列を stackalloc で作成するとスタックオーバーフローの原因となるため注意が必要です。

配列初期化のベストプラクティス

これまでの解説を踏まえ、状況に応じた最適な初期化方法をまとめます。

通常の初期化

基本的には C# 12 の コレクション式 を使って、可読性と将来のリファクタリングしやすさを優先します。

例: [1, 2, 3]

必要に応じて後で List 等へ変更しても差し替えが容易です。

要素数だけ決まっている場合

要素数が既に分かっているならサイズを指定して配列を作成します。

例: new int[size]

要素は既定値で初期化されるため、サイズのみが決まっているケースに適しています。

特定の値で埋めたい場合

配列を同じ値で埋める必要があるときは Array.Fill を活用し、ループによる手動代入を避けます。

例: Array.Fill(myArray, 42);

簡潔で意図が明確になり、安全かつ効率的です。

空の配列を返す場合

空の配列を返すときは Array.Empty<T>() またはコレクション式の [] を使用します。

これにより不要なメモリ割り当てを避けられます(Array.Empty<T>() はシングルトンの空配列を返します)。

多次元のデータを扱う場合

行の長さが一定なら多次元配列 [,] を選び、行ごとに長さが異なるならジャグ配列 [][] を使用します。

用途に応じてアクセス性能やメモリ配置の違いを考慮してください。

まとめ

C#の配列は、言語の進化とともにその書き方も洗練されてきました。

特に最新のコレクション式の導入により、従来の冗長な記述を排除し、直感的かつ安全に配列を初期化することが可能になっています。

本記事で解説した以下のポイントを振り返りましょう。

  • 配列は固定長の参照型であり、初期化には new 演算子や最新の [] 構文を用いる。
  • 未指定の要素には、型に応じたデフォルト値(0やnull)が自動的に設定される。
  • Array.Fill や LINQ を使うことで、一括初期化を効率的に行える。
  • 多次元配列とジャグ配列は構造が異なり、用途に応じて使い分ける必要がある。
  • パフォーマンスが求められる場面では Span<T>stackalloc の活用が有効。

配列はあらゆるプログラミングの基礎となる要素です。

これらの初期化手法をマスターすることで、より堅牢でパフォーマンスの高いC#アプリケーションの開発が可能になります。

日々のコーディングにおいて、最新の構文を積極的に取り入れ、美しく効率的なソースコードを目指してください。