いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Linq入門記-49 (LINQ To Object, FirstOrDefault, LastOrDefault, SingleOrDefault, DefaultIfEmpty)

今回は、以下のメソッドについてです。

  • FirstOrDefault拡張メソッド
  • LastOrDefault拡張メソッド
  • SingleOrDefault拡張メソッド
  • DefaultIfEmpty拡張メソッド

DefaultIfEmpty拡張メソッド以外は、前回記述した

  • First拡張メソッド
  • Last拡張メソッド
  • Single拡張メソッド

と同じ動きをするものです。
違いは、以下の点。

シーケンスに該当する要素が存在しない場合に規定値を返す。

つまり、該当する要素が存在しない場合でも例外が発生しないバージョンという事になります。


DefaultIfEmpty拡張メソッドは、シーケンスが空の場合に規定値またはデフォルト値を返すメソッドです。
このメソッド、LINQに外部結合を行う際に必須となります。
外部結合については、以下の記事にて記述していますのでご参考にまで。


書式は以下の通り。

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

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

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

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

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

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

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

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

DefaultIfEmpty拡張メソッドの2つ目の書式では、明示的なデフォルト値を設定できます。
これよく利用します。

var emptySequence = Enumerable.Empty<string>();
foreach (var item in emptySequence.DefaultIfEmpty("デフォルト値"))
{
    Console.WriteLine(item);
}

とすると、「デフォルト値」となります。明示的にデフォルトの値を指定していない場合は
規定値となるので、上記の場合はnullとなります。


以下、サンプルです。

    #region LinqSamples-46
    public class LinqSamples46 : IExecutable
    {
        public void Execute()
        {
            //
            // FirstOrDefault拡張メソッドは、First拡張メソッドと同じ動作をする。
            // 違いは、シーケンスに要素が存在しない場合に規定値を返す点である。
            //
            var emptySequence = Enumerable.Empty<string>();
            var languages     = new string[] { "csharp", "visualbasic", "java", "python", "ruby", "php", "c++" };
            
            try
            {
                // First拡張メソッドは要素が存在しない場合例外が発生する.
                emptySequence.First();
            }
            catch
            {
                Console.WriteLine("First拡張メソッドで例外発生");
            }
            
            Console.WriteLine("FirstOrDefaultの場合: {0}",            emptySequence.FirstOrDefault() ?? "null");
            Console.WriteLine("FirstOrDefaultの場合(predicate): {0}", languages.FirstOrDefault(item => item.EndsWith("z")) ?? "null");
            
            //
            // LastOrDefault拡張メソッドは、Last拡張メソッドと同じ動作をする。
            // 違いは、シーケンスに要素が存在しない場合に規定値を返す点である。
            //
            try
            {
                // Last拡張メソッドは要素が存在しない場合例外が発生する.
                emptySequence.Last();
            }
            catch
            {
                Console.WriteLine("Last拡張メソッドで例外発生");
            }
            
            Console.WriteLine("LastOrDefaultの場合: {0}",            emptySequence.LastOrDefault() ?? "null");
            Console.WriteLine("LastOrDefaultの場合(predicate): {0}", languages.LastOrDefault(item => item.EndsWith("z")) ?? "null");
            
            //
            // SingleOrDefault拡張メソッドは、Single拡張メソッドと同じ動作をする。
            // 違いは、シーケンスに要素が存在しない場合に規定値を返す点である。
            //
            try
            {
                // Last拡張メソッドは要素が存在しない場合例外が発生する.
                emptySequence.Single();
            }
            catch
            {
                Console.WriteLine("Single拡張メソッドで例外発生");
            }
            
            Console.WriteLine("SingleOrDefaultの場合: {0}",            emptySequence.SingleOrDefault() ?? "null");
            Console.WriteLine("SingleOrDefaultの場合(predicate): {0}", languages.SingleOrDefault(item => item.EndsWith("z")) ?? "null");
            
            //
            // DefaultIfEmpty拡張メソッドは、シーケンスが空の場合に規定値を返すメソッド。
            //
            // シーケンスに要素が存在する場合は、そのままの状態で返す。
            // LINQにて外部結合を行う際に必須となるメソッド。
            //
            Console.WriteLine("================ DefaultIfEmpty ====================");
            
            var emptyIntegers = Enumerable.Empty<int>();
            foreach (var item in emptyIntegers.DefaultIfEmpty())
            {
                Console.WriteLine("基本型の場合: {0}", item);
            }
            
            foreach (var item in emptySequence.DefaultIfEmpty())
            {
                Console.WriteLine("参照型の場合: {0}", item ?? "null");
            }
            
            foreach (var item in languages.DefaultIfEmpty())
            {
                Console.WriteLine(item ?? "null");
            }
            
            foreach (var item in emptySequence.DefaultIfEmpty("デフォルト値"))
            {
                Console.WriteLine(item ?? "null");
            }
        }
    }
    #endregion


実行結果は以下の通りです。

  First拡張メソッドで例外発生
  FirstOrDefaultの場合: null
  FirstOrDefaultの場合(predicate): null
  Last拡張メソッドで例外発生
  LastOrDefaultの場合: null
  LastOrDefaultの場合(predicate): null
  Single拡張メソッドで例外発生
  SingleOrDefaultの場合: null
  SingleOrDefaultの場合(predicate): null
  ================ DefaultIfEmpty ====================
  基本型の場合: 0
  参照型の場合: null
  csharp
  visualbasic
  java
  python
  ruby
  php
  c++
  デフォルト値

================================
過去の記事については、以下のページからご参照下さい。