C#において文字列の集合を扱う「string配列」は、あらゆるアプリケーション開発で頻繁に登場する基本的なデータ構造です。
かつてのC#では冗長な記述が必要だった配列の初期化も、言語のバージョンアップに伴い、より簡潔で直感的な記述が可能になりました。
特にC# 12で導入された「コレクション式」は、配列の初期化における決定版とも言える構文であり、モダンな開発スタイルでは欠かせない知識となっています。
本記事では、C#におけるstring配列の初期化手法を網羅的に解説します。
伝統的な宣言方法から、最新の[]構文、空配列の効率的な作成、さらには多次元配列やジャグ配列の初期化まで、テクニカルライターの視点で詳しく掘り下げていきます。
これからC#を学ぶ方はもちろん、最新の仕様を再確認したいベテランエンジニアの方も、ぜひ参考にしてください。
string配列の基本と宣言の仕組み
C#における配列は、同一の型を持つデータをメモリ上の連続した領域に配置する参照型のオブジェクトです。
string配列の場合、各要素には文字列への参照が格納されます。
配列を利用する際には、まず「配列のサイズ(要素数)」を決定し、メモリを確保する必要があります。
基本的な宣言と初期化の構文は、インスタンス化を行うnew演算子を使用するのが一般的です。
サイズを指定して初期化する
配列の要素数があらかじめ決まっているものの、中身が後から決まる場合は、要素数を指定してインスタンスを作成します。
// 要素数5のstring配列を宣言
string[] messages = new string[5];
// 各要素に値を代入
messages[0] = "Hello";
messages[1] = "World";
// 実行結果の確認
foreach (var msg in messages)
{
// 未代入の要素は null になる
Console.WriteLine(msg == null ? "(null)" : msg);
}
Hello
World
(null)
(null)
(null)
この方法で初期化した場合、各要素は型に応じたデフォルト値で埋められます。
stringは参照型であるため、初期値は空文字列(””)ではなく null になる点に注意が必要です。
プログラム実行時に NullReferenceException を防ぐため、適切な値で埋めるか、nullを許容する設計にする必要があります。
配列リテラルを用いた初期化
宣言と同時に具体的な値を代入する場合、配列リテラルを用いることでコードを簡潔に記述できます。
従来の初期化構文
C# 1.0から存在する最も基本的な初期化方法です。
// 型を明示し、値を中括弧で囲む
string[] fruits = new string[] { "Apple", "Banana", "Orange" };
この形式は、右辺で型を明示しているため非常に明確です。
また、以下のように型推論(var)と組み合わせることも可能です。
// varキーワードを使用した初期化
var animals = new string[] { "Dog", "Cat", "Bird" };
宣言時の省略形式
配列の型が左辺で明示されている場合に限り、右辺の new string[] を省略して記述することができます。
// 省略した記述方法
string[] colors = { "Red", "Green", "Blue" };
この書き方は非常にシンプルで、古くから多くのプロジェクトで採用されてきました。
しかし、この構文は「宣言と同時に初期化する場合」にしか使えないという制限があります。
例えば、フィールドで宣言した変数に対して後からこの構文で値を代入することはできません。
C# 12最新構文:コレクション式
C# 12において、配列やリストの初期化を劇的に進化させる「コレクション式(Collection Expressions)」が登場しました。
これにより、あらゆるコレクションを共通の [] 構文で記述できるようになりました。
コレクション式による初期化
従来の {} の代わりに [] を使用します。
// C# 12以降の推奨される書き方
string[] names = ["Alice", "Bob", "Charlie"];
// 代入時にも使用可能
names = ["Dave", "Eve"];
コレクション式の優れた点は、ターゲット型推論が働くことです。
左辺が string[] であれば配列として、List<string> であればリストとして初期化されます。
これにより、データの構造を変更した際でも右辺の書き方を変える必要がなくなりました。
スプレッド演算子との組み合わせ
コレクション式では、既存の配列を別の配列の中に展開する「スプレッド演算子(..)」が使用できます。
string[] groupA = ["User1", "User2"];
string[] groupB = ["User3", "User4"];
// 複数の配列を結合して初期化
string[] allUsers = [.. groupA, .. groupB, "Admin"];
foreach (var user in allUsers)
{
Console.WriteLine(user);
}
User1
User2
User3
User4
Admin
これまで Concat メソッドなどを使用していた処理が、直感的かつ宣言的に記述できるようになりました。
パフォーマンス面でも、コンパイラが最適なコードを生成するため、手動で配列を結合するよりも効率的です。
空の配列を初期化する最適な手法
メモリの節約や、APIの戻り値として「データがないこと」を示すために、要素数0の配列を作成する場面は多々あります。
パフォーマンスを考慮した初期化
空配列を作成する場合、以下の3つの方法が考えられます。
| 手法 | コード例 | 特徴 |
|---|---|---|
| new演算子 | new string[0] | 呼び出す度に新しいインスタンスを生成するため非効率。 |
| Array.Empty | Array.Empty<string>() | キャッシュされた単一のインスタンスを返すため、メモリ効率が良い。 |
| コレクション式 | [] | C# 12以降。内部的に Array.Empty 相当の最適化が行われる。 |
モダンなC#開発においては、可読性の観点から「[]」を使用するか、明示的に「Array.Empty<string>()」を使用するのがベストプラクティスです。
// 空の配列の初期化例
string[] emptyArray1 = Array.Empty<string>(); // 従来の推奨
string[] emptyArray2 = []; // C# 12以降の推奨
多次元配列とジャグ配列の初期化
複数の次元を持つデータの管理には「多次元配列」または「ジャグ配列(配列の配列)」を使用します。
string配列においても、行列データや階層構造を扱う際に利用されます。
多次元配列(矩形配列)
すべての行の長さが同じである配列です。
カンマを用いて宣言します。
// 2行3列のstring多次元配列
string[,] matrix = new string[2, 3]
{
{ "A1", "A2", "A3" },
{ "B1", "B2", "B3" }
};
Console.WriteLine(matrix[0, 1]); // 出力: A2
ジャグ配列(配列の配列)
各行の長さが異なる可能性のある「配列の配列」です。
// ジャグ配列の宣言と初期化
string[][] jagged =
[
["Short", "Row"],
["This", "is", "a", "longer", "row"],
["Single"]
];
// アクセス方法
Console.WriteLine(jagged[1][4]); // 出力: row
ジャグ配列は、各要素が独立した配列オブジェクトであるため、メモリレイアウトが連続していないという特徴があります。
一方で、各行のサイズを自由に変更できる柔軟性があるため、複雑なデータ構造にはジャグ配列が適しています。
特定のルールに基づいた初期化
プログラムで動的に配列を生成し、特定の文字列で初期化したい場合があります。
Enumerable.Repeat による初期化
同じ文字列で全要素を埋めたい場合に便利です。
using System.Linq;
// "N/A" という文字列で要素数10の配列を作成
string[] defaults = Enumerable.Repeat("N/A", 10).ToArray();
Console.WriteLine(defaults[5]); // 出力: N/A
Array.Fill による初期化
既存の配列の内容を高速に特定の値で上書きします。
string[] data = new string[5];
// すべての要素を "Init" で埋める
Array.Fill(data, "Init");
// 出力結果の確認
Console.WriteLine(string.Join(", ", data));
Init, Init, Init, Init, Init
Array.Fill は .NET Core 2.0 以降で利用可能で、パフォーマンスが非常に高く、ループで一つずつ代入するよりもクリーンなコードになります。
string配列初期化時の注意点とベストプラクティス
最後に、実務で役立つ注意点をいくつか紹介します。
1. null許容参照型への対応
C# 8.0以降、参照型でもnullを厳格に管理する「null許容参照型(Nullable Reference Types)」が導入されました。
#nullable enable
string[] nonNullNames = ["Alice", "Bob"]; // 各要素はnull不可
string?[] nullableNames = ["Alice", null]; // 要素にnullを許容する
プロジェクトの設定が <Nullable>enable</Nullable> になっている場合、string[] の要素に null を代入しようとすると警告が発生します。
配列初期化時には、その配列がnullを含む可能性があるかどうかを明確に型で表現しましょう。
2. string.Empty の活用
初期値として null を避けたい場合、空文字列を示す string.Empty を使用するのが一般的です。
// すべて空文字列で初期化
string[] blanks = Enumerable.Repeat(string.Empty, 5).ToArray();
""(リテラル)と string.Empty は実質的に同じですが、string.Empty を使うことで「意図的に空文字列にしている」という意図がコードを読む人に伝わりやすくなります。
3. パフォーマンスの選択
非常に大きな配列を頻繁に作成・破棄する場合、ガベージコレクション(GC)の負荷が問題になることがあります。
そのようなケースでは、配列の代わりに Span<T> や Memory<T> を検討するか、ArrayPool<T> を利用して配列を再利用する手法が有効です。
まとめ
C#におけるstring配列の初期化は、言語の進化とともに洗練されてきました。
- 基本的な初期化:要素数を指定する
new string[n]や、中括弧を用いたリテラル表記。 - 最新の初期化:C# 12以降は コレクション式
[]を使用するのが最も簡潔で推奨される。 - 空配列:
Array.Empty<string>()またはコレクション式[]を使い、メモリ効率を高める。 - 特殊な初期化:
Array.Fillや LINQ のEnumerable.Repeatを活用して、コードの意図を明確にする。 - 安全性:null許容参照型を意識し、
string?[]かstring[]かを適切に選択する。
状況に応じて最適な初期化手法を選択することで、コードの可読性は向上し、バグの少ない堅牢なプログラムを構築することができます。
特に最新のコレクション式は、今後のC#開発における標準となるため、積極的に取り入れていきましょう。
