trsing’s diary

勉強、読んだ本、仕事で調べたこととかのメモ。

C# LINQ Whereの戻り値メモ

やりたかったこと

LINQとnull条件演算子を合わせ、要素がなかったらnullを返してほしい。次の式でemp=nullになると期待。

>var lst = new List<int>();
>var emp = lst.Where(e => e > 0)?.Min();
シーケンスに要素が含まれていません
  + System.Linq.Enumerable.Min(IEnumerable<int>)

nullとはならずSystem.InvalidOperationExceptionが投げられました。

期待通りの結果を得る

まずは期待通りの結果を得る方法。

> var emp = lst.Where(e => e > 0).Min(e => (int?)e);
> emp
null

Minでnull許容型に変換すると要素がない場合nullが帰ってきます。

勘違い

Whereの結果、空ならnullと思っていましたがWhereListIteratorですね。

> var lst = new List<int>();
> var emp = lst.Where(e => e > 0);
> emp.GetType()
[System.Linq.Enumerable+WhereListIterator`1[System.Int32]]
class WhereListIterator<TSource> : Iterator<TSource>
abstract class Iterator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>

referencesource.microsoft.com

docs.microsoft.com

ドキュメントにも戻り値はIEnumerableであると書いてありますしね。勉強不足でした。遅延実行などについても詳細は把握してませんし。ぜんぜんわからない。俺は雰囲気でLINQをやっている。

その他適当に調べたこと

int?型のタイプを調べるときはGetType()を使わない。
> int? a = 0;
> a.GetType()
[System.Int32]

値が入ってるとint型

> int? a = null;
> a.GetType()
オブジェクト参照がオブジェクト インスタンスに設定されていません。
  + object.GetType()

nullだと例外。

Null許容型を識別する方法

docs.microsoft.com

配列のリスト
> var lst = new List<int[]>();
> lst.Where(e => e[0] > 0)
Enumerable.WhereListIterator<int[]> { }
> lst.Where(e => e[0] > 0).Min(e => e[0])
シーケンスに要素が含まれていません
  + System.Linq.Enumerable.Min(IEnumerable<int>)
> lst.Where(e => e[0] > 0).Min(e => e?[0])
null
DefaultIfEmpty

シーケンスが空の場合設定した値、設定しない場合規定値を持つシングルトンコレクションを返す。

docs.microsoft.com

> var lst = new List<int>();
> lst.Where(e=>e>0).DefaultIfEmpty()
DefaultIfEmptyIterator { 0 }
> lst.Where(e => e > 0).DefaultIfEmpty(5)
DefaultIfEmptyIterator { 5 }
> var lst = new List<int[]>();
> lst.Where(e => e[0] > 0).DefaultIfEmpty()
DefaultIfEmptyIterator { null }
> lst.Where(e=>e[0]>0).DefaultIfEmpty().Min()
null
> lst.Where(e => e[0] > 0).DefaultIfEmpty().Min(e=>e[0])
オブジェクト参照がオブジェクト インスタンスに設定されていません。
  + System.Linq.Enumerable.WhereSelectEnumerableIterator<TSource, TResult>.MoveNext()
  + System.Linq.Enumerable.Min(IEnumerable<int>)
  + System.Linq.Enumerable.Min<TSource>(IEnumerable<TSource>, Func<TSource, int>)
> lst.Where(e => e[0] > 0).DefaultIfEmpty().Min(e => e?[0])
null
> lst.Where(e => e[0] > 0).DefaultIfEmpty(new int[] { 1, 2 })
DefaultIfEmptyIterator { int[2] { 1, 2 } }

所感

今回の風古戦場やばない?ヤバババババムートじゃない?