はじめに:「Empty除外」は“中身がないものを先にどかす”テクニック
null を除外する話をしましたが、実務ではもう一つよく出てくる「やっかいな存在」がいます。
それが「空文字」や「空コレクション」、つまり「Empty」です。
文字列なら ""(空文字)
配列やリストなら「要素数 0」
これらは「存在はしているけど、中身がない」状態です。
業務ロジック的には「扱いたくない」「集計に入れたくない」ことが多いのに、
そのまま流してしまうと、あとで条件分岐が増えてコードがごちゃつきます。
そこで出てくるのが「Empty除外」という発想です。
「LINQ の入り口で、“中身が空のもの”を全部はじいてしまう」ことで、その後の処理をスッキリさせます。
ここでは、初心者向けに
文字列の Empty除外
コレクションの Empty除外
Empty除外用のユーティリティ拡張メソッド
業務での具体的な使いどころ
を、例題付きでかみ砕いて説明していきます。
文字列の Empty除外:IsNullOrEmpty / IsNullOrWhiteSpace を使いこなす
単純な空文字を除外する
まずは一番シンプルなパターンから。
using System;
using System.Collections.Generic;
using System.Linq;
var names = new List<string?>
{
"Alice",
"",
"Bob",
"",
"Charlie"
};
var nonEmptyNames = names
.Where(x => x != ""); // 空文字を除外
foreach (var name in nonEmptyNames)
{
Console.WriteLine(name);
}
C#出力はこうなります。
Alice
Bob
Charlie
""(空文字)がきれいに取り除かれています。
ここでの重要ポイントは、「null と "" は別物」ということです。null は「値そのものが存在しない」、"" は「値はあるけど中身が空」という違いがあります。
Empty除外では、この "" をどう扱うかを決めます。
実務では IsNullOrEmpty / IsNullOrWhiteSpace を使う
現実のデータは、"" だけでなく null や " "(空白だけ)も混ざります。
それをまとめて扱うために、string.IsNullOrEmpty や string.IsNullOrWhiteSpace を使うのが定番です。
var names = new List<string?>
{
"Alice",
"",
" ",
null,
"Bob"
};
var validNames = names
.Where(x => !string.IsNullOrWhiteSpace(x));
foreach (var name in validNames)
{
Console.WriteLine(name);
}
C#出力はこうなります。
Alice
Bob
ここでの重要ポイントは、「Empty除外と Null除外を一緒にやるなら、IsNullOrWhiteSpace が一番ラク」ということです。null、""、空白だけの文字列を、まとめて「中身なし」とみなせます。
コレクションの Empty除外:Count / Any を使う
要素数 0 のリストを除外する
次は「空のコレクション」を除外するパターンです。
using System;
using System.Collections.Generic;
using System.Linq;
var lists = new List<List<int>>
{
new List<int> { 1, 2, 3 },
new List<int>(), // 空
new List<int> { 4 },
new List<int>() // 空
};
var nonEmptyLists = lists
.Where(xs => xs.Any()); // 要素が 1 件以上あるものだけ
foreach (var xs in nonEmptyLists)
{
Console.WriteLine($"[{string.Join(", ", xs)}]");
}
C#出力はこうなります。
[1, 2, 3]
[4]
空のリストが除外されています。
ここでの重要ポイントは、「コレクションの Empty除外には Count == 0 ではなく Any() を使う」ということです。Any() は「1 件でもあれば true」で、パフォーマンス的にも意図的にも素直です。
子コレクションが Empty のものを除外する
もう少し実務寄りの例を見てみます。
public class Order
{
public string Id { get; set; } = "";
public List<OrderLine> Lines { get; set; } = new();
}
public class OrderLine
{
public string Item { get; set; } = "";
public int Amount { get; set; }
}
C#var orders = new List<Order>
{
new Order
{
Id = "O001",
Lines = new List<OrderLine>
{
new OrderLine { Item = "A", Amount = 100 }
}
},
new Order
{
Id = "O002",
Lines = new List<OrderLine>() // 空
}
};
C#「明細が 1 件もない注文は、集計対象から外したい」という場合、こう書けます。
var validOrders = orders
.Where(o => o.Lines.Any());
foreach (var o in validOrders)
{
Console.WriteLine(o.Id);
}
C#出力はこうなります。
O001
ここでの重要ポイントは、「Empty除外は“業務的に意味のないデータを早めに落とす”ために使う」ということです。
「明細ゼロの注文は集計しない」といったルールを、そのまま LINQ に落とし込めます。
Empty除外用のユーティリティ拡張メソッドを作る
文字列用:NotEmpty / NotNullOrWhiteSpace
毎回 Where(x => !string.IsNullOrWhiteSpace(x)) と書くのは長いので、
ユーティリティとして 1 本にまとめておくと読みやすくなります。
using System.Collections.Generic;
using System.Linq;
public static class StringFilterExtensions
{
public static IEnumerable<string> NotNullOrWhiteSpace(
this IEnumerable<string?> source)
{
return source.Where(x => !string.IsNullOrWhiteSpace(x))!;
}
}
C#使い方はこうです。
var names = new List<string?>
{
"Alice",
"",
" ",
null,
"Bob"
};
var validNames = names.NotNullOrWhiteSpace();
foreach (var name in validNames)
{
Console.WriteLine(name);
}
C#ここでの重要ポイントは、「メソッド名に“意図”を乗せる」ことです。NotNullOrWhiteSpace() と書いてあれば、「ここで null と空文字と空白だけを落としているんだな」と一目で分かります。
コレクション用:NotEmpty
コレクションに対しても同じ発想で拡張メソッドを作れます。
using System.Collections.Generic;
using System.Linq;
public static class CollectionFilterExtensions
{
public static IEnumerable<IEnumerable<T>> NotEmpty<T>(
this IEnumerable<IEnumerable<T>> source)
{
return source.Where(xs => xs.Any());
}
}
C#使い方はこうです。
var lists = new List<List<int>>
{
new List<int> { 1, 2 },
new List<int>(),
new List<int> { 3 }
};
var nonEmptyLists = lists.NotEmpty();
foreach (var xs in nonEmptyLists)
{
Console.WriteLine($"[{string.Join(", ", xs)}]");
}
C#ここでの重要ポイントは、「NotEmpty() という名前だけで、“空のコレクションを除外している”ことが伝わる」ということです。Where(xs => xs.Any()) よりも、業務コードとして読みやすくなります。
Null除外と Empty除外を組み合わせる
文字列の「null または空」をまとめて落とす
実務では、「null も空も空白だけも全部 NG」ということが多いです。
その場合は、Null除外と Empty除外を一緒にやります。
var names = new List<string?>
{
"Alice",
"",
" ",
null,
"Bob"
};
var validNames = names
.Where(x => !string.IsNullOrWhiteSpace(x));
foreach (var name in validNames)
{
Console.WriteLine(name);
}
C#あるいは、ユーティリティを使うならこうです。
var validNames = names.NotNullOrWhiteSpace();
C#ここでの重要ポイントは、「“null を許さないのか”“空文字を許さないのか”を最初に決めて、入り口でまとめて落とす」ということです。
後ろの処理で毎回 if を書くより、パイプラインの最初でフィルターしてしまったほうが、コードがきれいになります。
子コレクションが null または Empty のものを除外する
さきほどの Order の例で、Lines が null のこともある場合を考えます。
public class Order
{
public string Id { get; set; } = "";
public List<OrderLine>? Lines { get; set; }
}
C#var orders = new List<Order>
{
new Order
{
Id = "O001",
Lines = new List<OrderLine>
{
new OrderLine { Item = "A", Amount = 100 }
}
},
new Order
{
Id = "O002",
Lines = null
},
new Order
{
Id = "O003",
Lines = new List<OrderLine>()
}
};
C#「明細が 1 件もない注文(null も Empty も)は除外したい」なら、こう書けます。
var validOrders = orders
.Where(o => o.Lines != null && o.Lines.Any());
foreach (var o in validOrders)
{
Console.WriteLine(o.Id);
}
C#出力はこうなります。
O001
ここでの重要ポイントは、「Null除外と Empty除外をセットで考える」ということです。null だけ見ていると Empty を見落とし、Empty だけ見ていると null で落ちる、という事故が起きがちです。
業務での Empty除外の典型パターン
例1:検索条件の「空文字」を除外する
検索画面から複数条件が飛んでくるとき、
「ユーザーが入力していない項目(空文字)は条件に含めない」というのはよくある要件です。
public class SearchCondition
{
public string? Name { get; set; }
public string? Email { get; set; }
}
C#IQueryable<User> ApplyCondition(IQueryable<User> query, SearchCondition cond)
{
if (!string.IsNullOrWhiteSpace(cond.Name))
{
query = query.Where(u => u.Name.Contains(cond.Name));
}
if (!string.IsNullOrWhiteSpace(cond.Email))
{
query = query.Where(u => u.Email.Contains(cond.Email));
}
return query;
}
C#ここでの重要ポイントは、「Empty除外をしないと、“空文字で検索する”という意味不明な条件が付いてしまう」ということです。IsNullOrWhiteSpace で「実質的に入力されていない条件」を落としてから、クエリを組み立てます。
例2:ログやレポートから「中身のない行」を除外する
ログファイルやレポートを LINQ で加工するとき、
「空行」や「項目が全部空の行」は無視したいことが多いです。
var lines = File.ReadAllLines("log.txt");
var validLines = lines
.Where(x => !string.IsNullOrWhiteSpace(x));
foreach (var line in validLines)
{
Console.WriteLine(line);
}
C#ここでの重要ポイントは、「Empty除外は“ノイズを先に落とす”ためのフィルター」として使える、ということです。
ノイズを落としてから本題の処理をすると、ロジックがシンプルになります。
まとめ:「Empty除外ユーティリティ」は“中身のないものを早めに追い出すフィルター”
Empty除外の本質は、
「後ろの処理で“中身がないケース”を毎回気にするくらいなら、
最初に中身のないものを全部はじいてしまおう」
という発想です。
押さえておきたいポイントをもう一度整理すると、
文字列の Empty除外には !string.IsNullOrWhiteSpace(x) が実務的に便利
Null除外と Empty除外を一緒にやるなら、IsNullOrWhiteSpace 一択でいい場面が多い
コレクションの Empty除外には xs.Any() を使う(Count > 0 より素直)NotNullOrWhiteSpace() や NotEmpty() のような拡張メソッドを用意すると、意図が読みやすくなる
Empty除外は「ノイズを先に落とす」ための設計として、パイプラインの入り口に置く
ここまで腹落ちしていれば、
「とりあえず全部流して、途中で if でがんばる」段階から卒業して、
“null と empty を早めに追い出した、きれいな LINQ パイプライン”を、自分で組めるようになります。
