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

効率的なデータ管理を行う上で配列の理解は欠かせませんが、C#の進化に伴い、その初期化方法や操作手法は多岐にわたるようになりました。

本記事では、C#における配列の初期化と要素数の指定方法について、基礎から最新のC# 12で導入された構文までを、エンジニアが実務で直面する視点から詳しく解説します。

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

C#の配列は参照型であり、ヒープ領域にメモリが割り当てられます。

配列を宣言する際には、格納する要素の型を指定し、その後に [] を記述します。

配列は固定長であり、一度インスタンス化(初期化)されると、その要素数を動的に変更することはできません。

基本的な宣言の構文は以下の通りです。

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

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

この段階では、配列の変数が定義されただけであり、実体(インスタンス)は生成されていません。

そのため、この変数にアクセスしようとすると、実行時に NullReferenceException が発生します。

配列を利用するためには、必ず初期化(インスタンス化)の手順が必要となります。

配列の初期化方法:基本から最新構文まで

配列の初期化には、要素数だけを指定する方法や、初期値を同時に代入する方法など、いくつかのパターンが存在します。

1. 要素数を指定した初期化

最も標準的な方法は、 new 演算子を使用して要素数を明示的に指定する方法です。

この場合、各要素は型に応じたデフォルト値(数値型なら 0、参照型なら null)で自動的に初期化されます。

C#
using System;

class Program
{
    static void Main()
    {
        // 要素数5の整数型配列を初期化
        int[] numbers = new int[5];

        // 各要素のデフォルト値を確認
        foreach (var n in numbers)
        {
            Console.WriteLine(n);
        }
    }
}
実行結果
0
0
0
0
0

2. 配列初期化子を用いた初期化

配列の宣言と同時に具体的な値を代入したい場合は、配列初期化子{} )を使用します。

この方法では、記述された要素の数から自動的に要素数が決定されるため、要素数を数値で指定する必要はありません。

C#
// 配列初期化子を使用した例
int[] numbers = new int[] { 10, 20, 30, 40, 50 };

// さらに短縮した書き方
int[] numbers Short = { 10, 20, 30, 40, 50 };

この短縮記法は非常に一般的ですが、フィールドの初期化やローカル変数の宣言時のみ利用可能であり、メソッドの引数に直接渡す際などは new int[] { ... } の形式が必要になる場合があります。

3. 暗黙的に型指定された配列(varの利用)

C#の型推論機能( var )を利用することで、配列の型指定を簡略化できます。

ただし、この場合は右辺で必ず new[] を明示する必要があります。

C#
// 型推論を利用した配列の初期化
var prices = new[] { 100.5, 200.0, 350.75 };
// この場合、pricesは double[] 型として推論されます

4. 最新の「コレクション式」による初期化(C# 12以降)

C# 12から導入されたコレクション式(Collection Expressions)は、配列の初期化における最も洗練された方法です。

従来の new[]{} を使わずに、 [] だけで記述が可能になりました。

C#
using System;

class Program
{
    static void Main()
    {
        // C# 12以降の最新の書き方
        int[] numbers = [1, 2, 3, 4, 5];
        string[] tags = ["C#", ".NET", "Programming"];

        // 空の配列もシンプルに記述可能
        int[] emptyArray = [];

        Console.WriteLine($"要素数: {numbers.Length}");
        Console.WriteLine($"最初の要素: {tags[0]}");
    }
}
実行結果
要素数: 5
最初の要素: C#

コレクション式は、可読性が高いだけでなく、内部的に最適化されたコードへとコンパイルされるため、パフォーマンス面でも推奨される書き方となっています。

配列の要素数(長さ)の取得と操作

配列は固定長であるため、作成後に要素数を変えることはできません。

しかし、プログラム実行中に現在の配列の長さを知ることは非常に重要です。

LengthプロパティとCountプロパティの違い

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

プロパティ名説明
Length配列の全次元の総要素数を取得します( int 型)。
LongLength巨大な配列(2GB以上)を扱う場合に、要素数を long 型で取得します。
Rank配列の次元数を取得します。

C#の List<T> クラスでは Count プロパティを使用しますが、配列には Count プロパティは存在しないICollection を介さない限り直接は見えない)点に注意してください。

要素数の変更をシミュレートする(Array.Resize)

配列のサイズを「変更」したい場合、実際には新しいサイズの配列を別に確保し、古い内容をコピーするという手順が必要になります。

これを簡便に行うのが Array.Resize メソッドです。

C#
using System;

class Program
{
    static void Main()
    {
        int[] data = [1, 2, 3];
        Console.WriteLine($"変更前サイズ: {data.Length}");

        // サイズを5に変更(内部で新しい配列が生成される)
        Array.Resize(ref data, 5);
        data[3] = 4;
        data[4] = 5;

        Console.WriteLine($"変更後サイズ: {data.Length}");
        Console.WriteLine(string.Join(", ", data));
    }
}
実行結果
変更前サイズ: 3
変更後サイズ: 5
1, 2, 3, 4, 5

ただし、 Array.Resizeコストの高い操作であるため、頻繁にサイズ変更が発生する場合は List<T> の使用を検討すべきです。

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

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

多次元配列(矩形配列)

多次元配列は、各次元の要素数が一定である配列です。

カンマ , を用いて宣言します。

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

// 特定の要素にアクセス
int value = matrix[1, 2]; // 6

ジャグ配列(配列の配列)

ジャグ配列は、各行によって要素数が異なる構造を持たせることができます。

[][] を使用して宣言します。

C#
// ジャグ配列の初期化
int[][] jaggedArray = new int[3][];

jaggedArray[0] = [1, 2];
jaggedArray[1] = [3, 4, 5, 6];
jaggedArray[2] = [7];

// 要素へのアクセス
Console.WriteLine(jaggedArray[1][2]); // 5

多次元配列はメモリ効率が良い反面、.NETのランタイム最適化(JITコンパイル)においてはジャグ配列の方がアクセス速度が速いという特性があります。

パフォーマンスが極めて重要な数値計算などでは、この違いが影響することがあります。

配列操作の応用と最新機能

近年のC#では、配列をより安全かつ効率的に扱うための機能が強化されています。

インデックスと範囲(Index / Range)

C# 8.0から導入されたインデックス( ^ )と範囲( .. )演算子を使うと、配列の末尾からの指定や部分的な抽出が直感的に行えます。

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

// 末尾から1番目の要素(5)
int last = source[^1];

// 1番目から4番目の「手前」までを取得(1, 2, 3)
int[] slice = source[1..4];

Span<T> を利用した効率的な初期化

大量のデータや、スタック領域に割り当てられたメモリを効率的に扱いたい場合、 Span<T> が威力を発揮します。

配列の一部を参照する際、従来の Substring やコピーと異なり、新しいメモリ確保(アロケーション)を発生させずに操作が可能です。

C#
using System;

class Program
{
    static void Main()
    {
        int[] heavyData = new int[1000];
        // 配列の一部をスライスとして参照
        Span<int> span = heavyData.AsSpan(100, 50);
        
        // スライス経由で値を書き換えると元の配列も変わる
        span[0] = 999;
        Console.WriteLine(heavyData[100]); // 999
    }
}

パフォーマンスを意識した配列の初期化テクニック

実務レベルの開発では、配列の初期化コストが無視できない場合があります。

以下のテクニックはパフォーマンス最適化において非常に有効です。

Array.Empty<T>() の活用

要素数0の配列が必要な場合、 new int[0] と書くと、呼び出すたびに新しいインスタンスが生成されてしまいます。

代わりに Array.Empty<int>() を使用すると、内部でキャッシュされた単一のインスタンスが使い回されるため、ガベージコレクション(GC)の負荷を軽減できます。

C#
// 推奨されない例
int[] bad = new int[0];

// 推奨される例
int[] good = Array.Empty<int>();

Enumerable.Repeat による一括初期化

特定の初期値で配列を埋め尽くしたい場合は、 System.LinqEnumerable.Repeat が便利です。

C#
using System.Linq;

// 値「-1」で要素数100の配列を作成
int[] initialValues = Enumerable.Repeat(-1, 100).ToArray();

ただし、これも内部的にはループを回してコピーを行っているため、極限のパフォーマンスが必要な場合は new int[100] で作成した後に span.Fill(-1) を使用するのが最速です。

まとめ

C#の配列は、言語の進化と共にその記述方法が洗練されてきました。

  • 基本: new 型[要素数] で要素数を指定して初期化。
  • 最新: C# 12以降は [] (コレクション式)による簡潔な記述が推奨される。
  • 構造: 行列には多次元配列( [,] )、柔軟なサイズ変更にはジャグ配列( [][] )を使い分ける。
  • 応用: メモリ効率を重視する場合は Span<T>Array.Empty<T>() を活用する。

配列は固定長であるという制約を理解し、適切な初期化手法を選択することで、コードの可読性と実行速度の両立が可能になります。

まずは基本となるコレクション式をマスターし、要件に応じて Span<T> や多次元配列などの高度な機能を組み合わせていくのが、現代的なC#プログラミングのベストプラクティスと言えるでしょう。