FirstOrDefault実行後のnull判定を簡略化する拡張メソッド
IEnumerable<Bar>.FirstOrDefaultを実行後に、nullでなければBarの値を取得する等の場合、
var bars = new Bar[] { ... }; var bar = bars.FirstOrDefault(b => b.Name == "ABC"); // or // var bar = bars.Where(b => b.Name == "ABC").FirstOrDefault(); var hoge = ""; if (bar != null) hoge = bar.Hoge;
もしくは、
var bars = new Bar[] { ... }; var hoge = bars.Where(b => b.Name == "ABC").Select(b => b.Hoge).FirstOrDefault();
のように書いていました。 その後、nullだったら何もせず、nullでないなら処理が続くような場合は、
var foos = new Foo[] { ... }; var bars = new Bar[] { ... }; var hoge = bars.Where(b => b.Name == "ABC").Select(b => b.Hoge).FirstOrDefault(); if (hoge != null) { // 以下hogeを使用してコーディング var id = foos.Where(f => f.Hoge == hoge).Select(f => f.Id).FirstOrDefault(); if (id != null) { //以下idを使用してコーディング } }
となります。nullでないなら処理が進み、またその処理の中でFirstOrDefaultをして、またまたnullでないなら...と書くのがめんどいです。 また、一つの処理にまとめた場合、
var foos = new Foo[] { ... }; var bars = new Bar[] { ... }; var foo = foos.Where(f => f.Hoge == bars.Where(b => b.Name == "ABC").Select(b => b.Hoge).FirstOrDefault()).Select(f => f.Id).FirstOrDefault();
barsのFirstOrDefaultがnullでも処理が続いちゃうんですよね。nullの場合、例外が発生します。 処理の流れ的にも最初にfoosから始まるのも嫌です。 なので、私は以下の拡張メソッドを使用しています。
internal static class EnumerableExtensions { public static TResult FirstOrDefaultAndGet<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> func) { if (source == null) return default(TResult); var ret = source.FirstOrDefault(); if (ret == null || ret.Equals(default(TSource))) return default(TResult); return func(ret); } public static TResult FirstOrDefaultAndGet<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, Func<TSource, TResult> func) { if (source == null) return default(TResult); var ret = source.FirstOrDefault(predicate); if (ret == null || ret.Equals(default(TSource))) return default(TResult); return func(ret); } }
これを使用すると上記の例を、
var foos = new Foo[] { ... }; var bars = new Bar[] { ... }; var id = bars.Where(b => b.Name == "ABC").FirstOrDefaultAndGet(b => foos.Where(f => f.Hoge == b.Hoge).FirstOrDefaultAndGet(f => f.Id));
このように1文で書けますし、どこかでnullが発生した段階で処理を止めることができます。