はじめに:「Null除外」は“ゴミを先に掃き出してから考える”テクニック
業務コードを書いていると、ほぼ確実にこうなります。
データのどこかに null が紛れ込むnull を意識せずにプロパティにアクセスするNullReferenceException で落ちる
この「null に毎回ビクビクする状態」から抜け出すための、超シンプルで強力な考え方が「Null除外」です。
つまり、「LINQ の入り口で null を全部はじいてしまう」ことで、その後の処理をスッキリさせる、という発想です。
ここでは、初心者向けに
Where での基本的な Null除外OfType<T> を使った参照型の Null除外
「Null除外用ユーティリティ拡張メソッド」を自作する
業務コードでの具体的な使いどころ
を、例題付きでかみ砕いて説明していきます。
一番基本の形:Where で null を弾く
まずは「null じゃないものだけ」に絞る
一番ストレートな Null除外は、Where(x != null) です。
using System;
using System.Collections.Generic;
using System.Linq;
var names = new List<string?>
{
"Alice",
null,
"Bob",
null,
"Charlie"
};
var nonNullNames = names
.Where(x => x != null);
foreach (var name in nonNullNames)
{
Console.WriteLine(name);
}
C#出力はこうなります。
Alice
Bob
Charlie
null がきれいに取り除かれています。
ここでの重要ポイントは、「Where(x != null) を一発かませるだけで、“この先は null を気にしなくていい世界”を作れる」ということです。
この「入り口で掃除する」感覚が、Null除外の基本です。
でも型は string? のまま、という問題
上のコード、実は型的にはまだ少し気持ち悪いです。
IEnumerable<string?> nonNullNames = names.Where(x => x != null);
C#C# の型システム的には、「Where(x != null) しても、型は string? のまま」です。
つまり、「実際には null はもういないのに、型上は“いるかもしれない”ことになっている」状態です。
このギャップを埋めるために、もう一歩踏み込んだ Null除外テクニックが出てきます。
参照型の Null除外に便利な OfType<T>
OfType<string>() で「null じゃない string だけ」にする
LINQ には OfType<T>() というメソッドがあります。
本来は「特定の型だけを取り出す」ためのものですが、参照型に対しては「null を除外する」効果もあります。
var names = new List<string?>
{
"Alice",
null,
"Bob",
null,
"Charlie"
};
var nonNullNames = names
.OfType<string>(); // ここがポイント
foreach (var name in nonNullNames)
{
Console.WriteLine(name);
}
C#これも出力は同じです。
Alice
Bob
Charlie
ただし、型が違います。
IEnumerable<string> nonNullNames = names.OfType<string>();
C#ここでの重要ポイントは、「OfType<string>() を使うと、“null じゃない string だけ”になり、型も string(非 null)になる」ということです。
つまり、「実際の中身」と「型の世界」がきちんと一致します。
Where と OfType の違いを整理する
Where(x != null)
動き:null を除外する
型:IEnumerable<string?> のまま(nullable のまま)
OfType<string>()
動き:null を除外する
型:IEnumerable<string>(非 nullable)になる
「この先、null を気にせずに書きたい」なら、OfType<T> のほうが気持ちいいことが多いです。
Null除外ユーティリティ拡張メソッドを自作する
「NotNull」みたいな名前で 1 本用意しておく
毎回 OfType<string>() と書くのがちょっとダルい、という場合は、
「Null除外専用の拡張メソッド」を 1 本用意しておくと、コードがかなり読みやすくなります。
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
public static class NullFilterExtensions
{
public static IEnumerable<T> NotNull<T>(
this IEnumerable<T?> source)
where T : class
{
return source.Where(x => x is not null)!;
}
}
C#使い方はこうです。
var names = new List<string?>
{
"Alice",
null,
"Bob",
};
var nonNullNames = names.NotNull();
foreach (var name in nonNullNames)
{
Console.WriteLine(name);
}
C#ここでの重要ポイントは、「NotNull() という名前だけで、“ここで null を全部落としている”ことが一目で分かる」ことです。Where(x != null) よりも、意図がはっきり読み取れます。
C# 11 以降なら「Null除外注釈」で型ももっと賢くできる
より厳密にやるなら、[NotNull] や [MemberNotNullWhen] などの属性を使って、
「このメソッドを通ったあとは null じゃない」とコンパイラに教えることもできます。
ただ、初心者のうちは「OfType<T> か、シンプルな NotNull() 拡張メソッド」くらいで十分です。
大事なのは、「Null除外を“パターン”として持っておく」ことです。
プロパティにアクセスする前に Null除外しておく
例:User? のリストから Name を取り出す
よくあるパターンを見てみます。
public class User
{
public string Name { get; set; } = "";
}
var users = new List<User?>
{
new User { Name = "Alice" },
null,
new User { Name = "Bob" }
};
C#このとき、安直にこう書くと危険です。
var names = users
.Select(u => u.Name); // null のときに例外
C#u が null のときに u.Name で落ちます。
Null除外を先にやると、こうなります。
var names = users
.OfType<User>() // ここで null を除外
.Select(u => u.Name);
C#あるいは、自作の NotNull() を使うならこうです。
var names = users
.NotNull()
.Select(u => u.Name);
C#ここでの重要ポイントは、「プロパティにアクセスする前に Null除外を挟む」という習慣です。Select の中で ?. を乱発するより、「そもそも null を通さない」ほうが、ロジックがスッキリします。
SelectMany と組み合わせた Null除外
例:子コレクションが null のことがある場合
もう少しだけ実務寄りの例を出します。
public class Order
{
public string Id { get; set; } = "";
public List<OrderLine>? Lines { get; set; }
}
public class OrderLine
{
public string Item { get; set; } = "";
public int Amount { get; set; }
}
C#Lines が null のことがあるとします。
var orders = new List<Order>
{
new Order
{
Id = "O001",
Lines = new List<OrderLine>
{
new OrderLine { Item = "A", Amount = 100 },
new OrderLine { Item = "B", Amount = 200 },
}
},
new Order
{
Id = "O002",
Lines = null
}
};
C#全明細を 1 本のシーケンスにしたいとき、安直にこう書くと危険です。
var allLines = orders
.SelectMany(o => o.Lines); // Lines が null だと例外
C#Null除外を挟むと、こうなります。
var allLines = orders
.Select(o => o.Lines)
.OfType<List<OrderLine>>() // null の Lines を除外
.SelectMany(lines => lines);
C#あるいは、NotNull() を使うならこうです。
var allLines = orders
.Select(o => o.Lines)
.NotNull()
.SelectMany(lines => lines);
C#ここでの重要ポイントは、「SelectMany の前に、“子コレクションが null じゃないこと”を保証しておく」ということです。
Null除外をパターンとして持っていると、こういう場面で迷わなくなります。
「Null除外」を業務ユーティリティとしてどう位置づけるか
発想の転換:「null をどう扱うか」ではなく「null を通さない」
多くの初心者は、「null が来たらどうするか」を毎回 if で考えます。
if (x != null)
{
// …
}
C#でも、LINQ を使うときは発想を逆にして、
「そもそも null を流さないようにする」
と考えたほうが、コードがシンプルになります。
入り口で Where(x != null) や OfType<T>、NotNull() を挟む
その後の処理は「null じゃない前提」で書く
この「入り口で掃除してから流す」というパターンを、業務ユーティリティとしてチームで共有しておくと、NullReferenceException に怯える時間がかなり減ります。
「null を許す設計」か「null を許さない設計」かを決める
もう一歩踏み込むと、設計レベルの話になります。
このコレクションは、null を含んでいてもいいのか
含んでいてもいいなら、どこで除外するのか
そもそも null を入れないように設計できないか
例えば、「ユーザー一覧に null が混ざっている」のは、そもそも設計として怪しいかもしれません。
一方、「外部システムから来るデータで null が混ざるのは仕方ない」なら、
「受け取った瞬間に Null除外して、内部では null を扱わない」という方針も取れます。
ここでの重要ポイントは、「Null除外は“場当たり的な対処”ではなく、“設計の一部”として考える」ということです。
そのための道具として、Where(x != null)、OfType<T>、NotNull() といったユーティリティを持っておく、という位置づけです。
まとめ:「Null除外ユーティリティ」は“null を早めに追い出すためのフィルター”
Null除外の本質は、
「後ろの処理で毎回 null を気にするくらいなら、
最初に null を全部はじいてしまおう」
という発想です。
押さえておきたいポイントをもう一度整理すると、
Where(x => x != null) でシンプルに null を除外できる
参照型なら OfType<T>() を使うと、「中身も型も“非 null”」にできるNotNull() のような拡張メソッドを 1 本用意しておくと、意図が読みやすくなる
プロパティにアクセスする前に Null除外を挟む、という習慣をつける
Null除外は「設計の一部」として、どこでやるかを決めておく
ここまで腹落ちしていれば、
「とりあえず ?. を付けてごまかす」段階から卒業して、
“null を早めに追い出す、きれいな LINQ パイプライン”を、自分で組み立てられるようになります。
