C# Tips | コレクション・LINQ:空List生成

C# C#
スポンサーリンク

はじめに:「空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.Countforeach も 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 で初期化しておく」コードから卒業して、
“例外に振り回されないコレクション設計”に一歩進めます。

タイトルとURLをコピーしました