First メソッドの使い方

Deep Dive into LINQ #1

Posted on 2022-03-03
target: .NET 6

Deep Dive into LINQ シリーズ記事一覧

Part 1 - First メソッドの使い方

Part 2 - First メソッドを作ってみる

Part 3 - First メソッドの実装をざっくり読む

Part 4 - First メソッドのパフォーマンス特性

はじめに

この記事は Deep Dive into LINQ シリーズの第1回目の記事です。 Deep Dive into LINQ シリーズでは、 LINQ の内部実装やパフォーマンス特性について基本的なことから調べていきます。

まずは数回に分けて LINQ の First メソッドについて研究していきます。 最初に First メソッドを選んだのは、「初回」ということと、処理が比較的シンプルであるという理由からです。

今回は基本的な実装例を見ながら、 First メソッドと FirstOrDefault メソッドの使い方を見ていきます。

First メソッド

First メソッドとは、シーケンスの最初の要素を取得するメソッドです。 類似のメソッドとして、 FirstOrDefault メソッドもあります。 First メソッドは最も基本的な LINQ のメソッドの一つです。

条件なし First メソッド

最も単純な First メソッドの定義は以下のようになります。

public static TSource First<TSource>(this IEnumerable<TSource> source)

このメソッドはシーケンスの最初の要素を返します。もし最初の要素がない場合(すなわち、シーケンスが空っぽの場合)は InvalidOperationException が発生します。

string[] array = new[] { "first", "second", "third" };

// 最初の要素 "first" が返る
var first = array.First();
Assert.Equal("first", first);

// 要素が一つもない場合は InvalidOperationException が発生する
string[] emptyArray = Array.Empty<string>();

Assert.Throws<InvalidOperationException>(() =>
{
    _ = emptyArray.First();
});

条件付き First メソッド

また、「条件」を受け取るオーバーロードもあります。

public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

このメソッドは与えられた条件を満たす最初の要素を返します。 どの要素も条件を満たさない場合は InvalidOperationException が発生します。

int[] array = new[] { 1, 2, 3, 4, 5 };

// 最初の偶数 (2) を取得
var firstEven = array.First(x => x % 2 == 0);
Assert.Equal(2, firstEven);

// 条件に一致する要素がなければ InvalidOperationException が発生する
Assert.Throws<InvalidOperationException>(() =>
{
    // 10 より大きい要素は存在しない
    _ = array.First(x => x > 10);
});

条件なし FirstOrDefault メソッド

FirstOrDefault メソッドの定義は下記のようになっています。

public static TSource? FirstOrDefault<TSource>(this IEnumerable<TSource> source)

First メソッドとほとんど同じですが、 FirstOrDefault メソッドは空のシーケンスの場合に例外を発生させる代わりにデフォルト値を返します。

int[] array = new[] { 1, 2, 3, 4, 5 };

// 最初の要素 (1) が返る
var first = array.FirstOrDefault();
Assert.Equal(1, first);

int[] emptyArray = Array.Empty<int>();

// 空の配列に対してはデフォルト値 (0) が返る
var firstOfEmptyArray = emptyArray.FirstOrDefault();
Assert.Equal(0, firstOfEmptyArray);

// デフォルト値は指定することもできる
var firstOfEmptyArray2 = emptyArray.FirstOrDefault(100);
Assert.Equal(100, firstOfEmptyArray2);

また、シーケンスが空っぽの場合のデフォルト値を指定することもできます。 その場合は下記のメソッドが使われます。

public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue)

条件付き FirstOrDefault メソッド

FirstOrDefault にも条件を受け取るメソッドがあります。

public static TSource? FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

条件なしの場合と同様に、デフォルト値を指定することもできます。

public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, TSource defaultValue)

条件を満たす最初の要素を返し、条件を満たす要素が一つもなければデフォルト値を返します。

int[] array = new[] { 1, 2, 3, 4, 5 };

// 最初の偶数 (2) を取得
var firstEven = array.FirstOrDefault(x => x % 2 == 0);
Assert.Equal(2, firstEven);

// 条件に一致する要素がなければデフォルト値を返す
// 10 より大きい要素は存在しないので 0 が返る
var greaterThan10 = array.FirstOrDefault(x => x > 10);
Assert.Equal(0, greaterThan10);

// デフォルト値を指定することもできる
var greaterThan10Or999 = array.FirstOrDefault(x => x > 10, 999);
Assert.Equal(999, greaterThan10Or999);

まとめ

今回は FirstFirstOrDefault について、基本的な挙動や使い方を見てきました。 どちらも「最初の要素を返す」メソッドですが、要素がなかった場合の挙動について、

  • First メソッド: 例外 (InvalidOperationException) を投げる
  • FirstOrDefault メソッド: デフォルト値を返す(例外は発生しない)

という違いがあります。

次回からは First メソッドの内部実装やパフォーマンス特性などを調べていきます。