はじめに:「空List生成」は“安全な初期値”を手に入れるテクニック
業務コードでよくあるパターンとして、
List<string> names = null;
// ここで落ちる
int count = names.Count;
C#「とりあえず null で初期化しておいて、あとで代入するつもりだった」
この“とりあえず null”が、あとで NullReferenceException の原因になります。
ここで欲しいのは、
「最初から“空の List”を入れておくことで、null を扱わなくて済むようにする」
という発想です。
空の List は「要素が 0 個のコレクション」であって、null とは違います。
この違いをちゃんと意識できると、コレクションまわりのバグが一気に減ります。
基本形:new List<T>() で空の List を作る
一番素直な書き方
C# で空の List を作る一番基本の書き方は、これです。
var names = new List<string>();
C#この時点で names.Count は 0 ですが、names 自体は null ではありません。
Console.WriteLine(names.Count); // 0
names.Add("Alice");
Console.WriteLine(names.Count); // 1
C#ここでの重要ポイントは、「null ではない」ということです。names.Count も foreach も LINQ も、安心して呼べます。
パターン1:フィールドやプロパティを“空で初期化”しておく
悪い例:null で初期化してしまう
public class Order
{
public List<OrderItem> Items { get; set; } // どこかで null のままかも…
}
C#この書き方だと、Items に何も代入しなければ null のままです。
呼び出し側が order.Items.Count と書いた瞬間に落ちます。
良い例:コンストラクタで空 List を入れておく
public class Order
{
public List<OrderItem> Items { get; set; }
public Order()
{
Items = new List<OrderItem>(); // 最初から“空”で用意しておく
}
}
C#こうしておけば、Items は「要素 0 の List」であって、null ではありません。
var order = new Order();
Console.WriteLine(order.Items.Count); // 0(安全)
order.Items.Add(new OrderItem());
C#ここでの重要ポイントは、
「フィールドやプロパティは“null かもしれない”ではなく、“空かもしれない”状態にしておく」
という設計です。
“空”なら、ほとんどの操作が安全に動きます。
パターン2:LINQ の Empty と組み合わせる
Enumerable.Empty<T>() で“読み取り専用の空列”を作る
List<T> ではなく、IEnumerable<T> として扱いたいときは、Enumerable.Empty<T>() が使えます。
using System.Linq;
IEnumerable<string> names = Enumerable.Empty<string>();
C#これは「要素 0 の列」を返します。foreach で回しても何も出てきませんが、null ではありません。
foreach (var n in names)
{
Console.WriteLine(n); // 何も出ないが、例外にはならない
}
C#ここでの重要ポイントは、
「Enumerable.Empty<T>() は、new List<T>() と同じ“空”だが、
基本的に読み取り専用として使う」
ということです。Add などはできませんが、「結果が 0 件のクエリ」などを表すのに向いています。
パターン3:null かもしれないものを“空 List に正規化”する
拡張メソッドで「空 List 生成」とセットにする
前の「null 安全 List」と組み合わせると、
「null かもしれない列を、必ず空 List に変換する」ユーティリティが書けます。
using System.Collections.Generic;
using System.Linq;
public static class ListExtensions
{
public static List<T> ToSafeList<T>(this IEnumerable<T>? source)
{
return source?.ToList() ?? new List<T>();
}
}
C#使い方の例です。
IEnumerable<string>? maybeNames = GetNamesOrNull();
List<string> names = maybeNames.ToSafeList();
// ここから先、names は絶対に null ではない
Console.WriteLine(names.Count);
C#ここでの重要ポイントは、
「境界で一度だけ new List<T>() を呼び、内部では“null ではない List”として扱う」
という設計です。
空 List 生成は、“null を排除するための入り口”として使うと効果的です。
パターン4:初期容量を指定して空 List を作る
大量データが入ると分かっている場合
パフォーマンスを少し意識するなら、
「だいたい何件くらい入るか」が分かっているときに、
初期容量を指定して空 List を作ることもできます。
var users = new List<User>(capacity: 1000);
C#この時点では要素数は 0 ですが、
内部的な配列は 1000 件分を確保してくれます。
大量に Add する場合、途中で何度も配列を拡張するコストを減らせます。
ここでの重要ポイントは、
「new List<T>(N) は“要素 N 個”ではなく、“N 個分の容量を持つ空 List”」
だということです。
Count は 0 のままなので、誤解しないようにしてください。
実務で意識してほしいこと
「null で初期化する癖」をやめる
業務コードでよく見るのが、
List<string> names = null;
C#という書き方です。
これは「あとで絶対に代入するから」という前提で書かれがちですが、
どこかで代入を忘れた瞬間にバグになります。
代わりに、最初からこう書く癖をつけると安全です。
var names = new List<string>(); // とりあえず空
C#「まだデータがない」ことを表現したいなら、
null ではなく「要素 0 の List」で十分です。
「空」と「null」を意味的に分ける
空 List は「データはあるが、件数が 0」
null は「そもそも“データという概念”が存在しない、または取得に失敗した」
というように、意味が違います。
多くの業務システムでは、
「単に今はデータがないだけ」というケースがほとんどなので、
null よりも空 List を使うほうが自然です。
まとめ:「空List生成ユーティリティ」は“null を遠ざけるためのスタート地点”
押さえておきたいポイントを短くまとめると、こうなります。
new List<T>() は「要素 0 の List」であり、null ではない。
フィールドやプロパティは、コンストラクタで空 List に初期化しておくと安全。Enumerable.Empty<T>() は「読み取り専用の空列」として LINQ と相性が良い。
null かもしれない列は、境界で ToSafeList などを通して“空 List”に正規化してから使う。
この感覚が身につくと、
「とりあえず null で初期化しておく」コードから卒業して、
“例外に振り回されないコレクション設計”に一歩進めます。
