C#でLINQを使用した開発を進める中で、多くのエンジニアが一度は遭遇するのが「System.InvalidOperationException: Sequence contains no elements」という例外です。
このエラーは、シーケンス(コレクション)の中に要素が一つも存在しない状態で、要素の取得を試みた際に発生します。
実行時にこの例外がスローされると、アプリケーションが予期せず停止する原因となるため、適切な対策を講じることが重要です。
本記事では、この例外が発生する主な原因から、具体的な解決策、そして未然に防ぐための実装パターンまでを整理して解説します。
例外「Sequence contains no elements」が発生する原因
この例外の直接的な原因は、空のコレクションに対して、少なくとも1つの要素があることを前提としたLINQメソッドを呼び出すことにあります。
LINQ(Language Integrated Query)には、特定の条件に合致する要素を抽出するメソッドが多数用意されていますが、一部のメソッドは「要素が見つからないこと」を許容しません。
例えば、データベースから特定のIDを持つレコードを取得しようとした際に、該当するデータが存在しないケースなどが典型的な例です。
また、Whereメソッドでフィルタリングした結果が空になった後、そのまま要素取得メソッドを連結した場合にもこのエラーは発生します。
このエラーメッセージは、直訳すると「シーケンスに要素が含まれていません」という意味であり、プログラムのロジックと実際のデータの整合性が取れていないことを示唆しています。
例外をスローする代表的なLINQメソッド
どのようなメソッドがこの例外を発生させるのかを把握しておくことは、デバッグや予防において非常に役立ちます。
主に、以下のメソッドを空のシーケンスに対して実行した際に、例外がスローされます。
First():シーケンスの先頭要素を返すが、空の場合は例外。Last():シーケンスの最後の要素を返すが、空の場合は例外。Single():シーケンスの唯一の要素を返すが、空または複数要素がある場合は例外。Max():最大値を返すが、数値型以外のシーケンスや特定の条件下で空の場合は例外。Min():最小値を返すが、空の場合は例外。Average():平均値を算出するが、空のシーケンスでは計算不能として例外。
これらのメソッドを使用する際は、対象となるデータが空である可能性を常に考慮する必要があります。
First()メソッドによる例外の発生例
まずは、最も発生頻度が高い First() メソッドによるエラーの再現コードを確認してみましょう。
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
// 空のリストを作成
List<string> names = new List<string>();
// 要素が空の状態でFirst()を呼び出す
// ここで System.InvalidOperationException が発生する
string firstName = names.First();
Console.WriteLine(firstName);
}
}
Unhandled exception. System.InvalidOperationException: Sequence contains no elements
at System.Linq.ThrowHelper.ThrowNoElementsException()
at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
at Program.Main()
このコードでは、names リストが空であるにもかかわらず、最初の要素を取得しようとしたため例外が発生しています。
開発環境ではデータが入っていても、本番環境のデータ状況によっては空になる可能性があることに注意が必要です。
解決策1:OrDefault系のメソッドを使用する
この例外を回避する最も一般的かつ推奨される方法は、First() の代わりに FirstOrDefault() を使用することです。
「OrDefault」と名前の付くメソッドは、要素が存在しない場合に例外をスローするのではなく、その型のデフォルト値(参照型であれば null)を返します。
// First() の代わりに FirstOrDefault() を使用
string firstName = names.FirstOrDefault();
if (firstName == null)
{
Console.WriteLine("要素が見つかりませんでした。");
}
else
{
Console.WriteLine($"最初の名前: {firstName}");
}
同様に、Last() には LastOrDefault()、Single() には SingleOrDefault() が用意されています。
これにより、プログラムが突然停止することを防ぎ、要素がない場合の処理を安全に記述できるようになります。
解決策2:Any()メソッドで事前に存在チェックを行う
取得を試みる前に、シーケンスに要素が含まれているかどうかを Any() メソッドで確認する手法も有効です。
特に、取得だけでなく、その後の複雑なロジックを要素がある場合のみ実行したいシーンに適しています。
if (names.Any())
{
// 要素があることが確定しているため、安全にFirst()を呼べる
string firstName = names.First();
Console.WriteLine(firstName);
}
else
{
Console.WriteLine("リストは空です。");
}
ただし、大規模なデータソースや外部クエリに対して Any() と First() を別々に呼び出すと、二度のクエリ発行が発生しパフォーマンスに影響を与える場合があるため注意しましょう。
解決策3:DefaultIfEmpty()でデフォルト値を指定する
リストが空の場合に、特定の初期値を返したいときには DefaultIfEmpty() が便利です。
このメソッドを通すことで、シーケンスが空であっても「指定した1つの値を持つシーケンス」に変換されます。
var safeNames = names.DefaultIfEmpty("名無し").First();
Console.WriteLine(safeNames); // リストが空なら「名無し」が出力される
これにより、後続の First() が例外を投げる心配がなくなり、コードの記述もシンプルに保つことができます。
Single()メソッドにおける注意点
Single() メソッドは、「要素がちょうど1つだけ存在すること」を厳格にチェックします。
そのため、要素が0個の場合だけでなく、要素が2個以上ある場合も例外をスローします。
主キーによる検索など、一意性が保証されているデータ取得には SingleOrDefault() を使用するのが適切です。
一方で、単に「複数あってもいいが最初の1つが欲しい」という場合は FirstOrDefault() を選択すべきです。
集計メソッドでの例外対策
数値データの集計を行う Max(), Min(), Average() も、空のシーケンスに対して呼び出すと例外が発生します。
これを防ぐためには、事前に Cast や Select で null 許容型に変換してから呼び出すテクニックがよく使われます。
List<int> numbers = new List<int>();
// null許容型にキャストして実行することで、空の場合は null が返るようになる
int? maxVal = numbers.Cast<int?>().Max();
if (maxVal.HasValue)
{
Console.WriteLine($"最大値: {maxVal.Value}");
}
else
{
Console.WriteLine("データが存在しません。");
}
単に Max() を呼ぶと例外で止まってしまいますが、上記のように実装することで安全に処理を継続できます。
例外を回避するためのベストプラクティス
2026年現在のモダンなC#開発において、この例外を未然に防ぐための設計指針は以下の通りです。
まず、「空であることは例外的な状況か、それとも正常な状況か」を明確に区別してください。
もしデータが空であることがプログラムの論理的なバグを意味するのであれば、あえて First() を使い、例外を発生させて早期に問題を検知するのも一つの戦略です。
しかし、ユーザーの入力やデータベースの状態によって空になり得るのであれば、必ず FirstOrDefault() などの安全なメソッドを使用してください。
また、C#の「null許容参照型(Nullable Reference Types)」を有効にしている場合、FirstOrDefault() の戻り値が null になる可能性をコンパイラが警告してくれます。
この警告を無視せず、適切に null チェックや null 合体演算子(??)を活用して、堅牢なコードを記述しましょう。
まとめ
「System.InvalidOperationException: Sequence contains no elements」は、C#開発において非常によく見られるエラーですが、その原因と対策は明確です。
要素が空の可能性があるシーケンスに対しては、FirstOrDefault() や Any() を適切に使用することが解決の近道となります。
また、Single() と First() の挙動の違いを正しく理解し、データの特性に合わせたメソッド選択を行うことが、予期せぬバグの防止に繋がります。
エラーが発生した際は、スタックトレースからどのLINQメソッドが原因かを特定し、今回紹介した手法を適用してみてください。
日頃から空のコレクションに対するガード処理を意識することで、より安定したアプリケーションを構築できるはずです。
