LINQ - 空のシーケンスでMin/Max/Averageを使ったときのメモ

LINQで空のシーケンスに対してMin/Max/Averageを使うとInvalidOperationExceptionが発生する場合があります。言われるとそっかと思うんですが、SQLの感覚もあってかうっかりやっちゃうかなあと思ったのでちょっとメモしておきます。

たとえば、intの空のシーケンスに対してMinメソッドを使ってみます。この場合はInvalidOperationExceptionが発生します。int?の空のシーケンスだとnullが返ってきます。

// intの空のシーケンスの場合はInvalidOperationException
try {
    Enumerable.Empty<int>().Min();
} catch (InvalidOperationException exception) {
    Console.WriteLine($"{exception.Message}");
    //シーケンスに要素が含まれていません
}

// int?の空のシーケンスの場合はnull
var min = Enumerable.Empty<int?>().Min();
if (min == null) {
    Console.WriteLine($"{nameof(min)}はnull");
    //minはnull
}

selector関数を使ったオーバーロードも確認してみます。selector関数の戻り値がnull許容型かどうかでnullが返ってくる場合と例外が発生する場合があります。

// selector関数の戻り値が非nullの場合はInvalidOperationException
try {
    Enumerable.Empty<Tuple<DateTime>>().Min(tuple => tuple.Item1);
} catch (InvalidOperationException exception) {
    Console.WriteLine($"{exception.Message}");
    //シーケンスに要素が含まれていません
}

// selector関数の戻り値がnull許容型の場合はnull
var min = Enumerable.Empty<Tuple<DateTime?>>().Min(tuple => tuple.Item1);
if (min == null) {
    Console.WriteLine($"{nameof(min)}はnull");
    //minはnull
}

null許容型ではないシーケンスにMin/Max/Averageするときはシーケンスが空かどうかチェックしましょうってことですね。はい、気をつけます。