2016-05-09 12 views
0

System.Linq.Enumerable.DefaultIfEmptyの実装を追跡することで、このメソッドが使用されました。これは、次の趣のある詳細を除いて大丈夫になりますDefaultIfEmptyがこのように実装されているのはなぜですか?

// System.Linq.Enumerable 
[IteratorStateMachine(typeof(Enumerable.<DefaultIfEmptyIterator>d__90<>))] 
private static IEnumerable<TSource> DefaultIfEmptyIterator<TSource>(IEnumerable<TSource> source, TSource defaultValue) 
{ 
    using (IEnumerator<TSource> enumerator = source.GetEnumerator()) 
    { 
     if (enumerator.MoveNext()) 
     { 
      do 
      { 
       yield return enumerator.Current; 
      } 
      while (enumerator.MoveNext()); 
     } 
     else 
     { 
      yield return defaultValue; 
     } 
    } 
    IEnumerator<TSource> enumerator = null; 
    yield break; 
    yield break; 
} 

1)なぜコードは、配列が空でないことが確立された後、シーケンス全体を反復処理する必要があるのでしょうか?

2)なぜ収穫が最後に2回壊れますか?

3)enumeratorの最後にnullを明示的に設定する理由は何ですか?

私はこれでそれを残しているだろう:あなたが列挙を開始し、このコードは、列挙の別のレベルとして使用されているときに、全部を列挙しなければならないので

// System.Linq.Enumerable 
[IteratorStateMachine(typeof(Enumerable.<DefaultIfEmptyIterator>d__90<>))] 
private static IEnumerable<TSource> DefaultIfEmptyIterator<TSource>(IEnumerable<TSource> source, TSource defaultValue) 
{ 
    using (IEnumerator<TSource> enumerator = source.GetEnumerator()) 
    { 
     if (enumerator.MoveNext()) 
     { 
      do 
      { 
       yield return enumerator.Current; 
      } 
      // while (enumerator.MoveNext()); 
     } 
     else 
     { 
      yield return defaultValue; 
     } 
    } 
    // IEnumerator<TSource> enumerator = null; 
    yield break; 
    // yield break; 
} 
+4

は[このソース(http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,1ff6169a97a478bf,references)違って見えます。デコンパイラを信頼しないで、元のソースコードを見てください。 –

+0

ありがとうございます。 3つの質問のうちの最初の質問だけが残ります。 –

+0

最初の質問は簡単です: 'DefaultIfEmpty'には一つの目的があります:シーケンス全体を返すか、空の場合はデフォルト値を返します。 'strings.Where(somecondition).DefaultIfEmpty(" foo ")。First()'と書かれたシーケンスは完全には列挙されませんが、最初の要素が生成されるまでのみ列挙されます。 –

答えて

0

DefaultIfEmptyニーズ以下のように作用する:

  1. ソース列挙はエントリがない場合、それは単一の値を持つ列挙として機能する必要があります。デフォルト値

  2. ソースenumerableが空でない場合、ソースenumerableとして動作する必要があります。したがって、すべての値を生成する必要があります。

+0

ありがとうございます。問題のコメントに記述されているようにコードがうまく見えないのとは別に、この誤解もありました。あなたの答えはクリアされています。シーケンスが空でない場合は最初の要素を返す*もっと、全く意味をなさないと思われる。 :-) –

+0

あなたはようこそ。これは 'FirstOrDefault'メソッドのようになります。 –

+1

@ WaterCoolerv2:このメソッドは、連鎖したメソッドが失敗する可能性のある空のシーケンスに対する保護として理解されています。 'First'はその場合に' InvalidOperationException'を投げます。 'DefaultIfEmpty'の後は、安全に使うことができます。オーバーロードにより、空のシーケンスに対してカスタムのフォールバック値を提供できます。 –

1

yield return最初のコードは、この列挙子を使用しているコードでは値が1つしかないと考えられます。だからあなたはそこにあるすべてを列挙し、yield returnそれを転送しなければなりません。

もちろんreturn enumeratorを実行しても問題ありませんが、MoveNext()が呼び出された後では最初の値がスキップされるため、それは機能しません。値が存在するかどうかを確認するもう1つの方法がある場合は、これがその方法です。

1

なぜコードは、配列が空でないことが確立された後、シーケンス全体を反復することがありますか?

あなたはMSDNについてDefaultIfEmtpy戻り値で読み取ることができたよう:

ソースが空の場合TSource型のデフォルト値が含まれているIEnumerable<T>オブジェクト。そうでなければ、source。

したがって、列挙型が空の場合、結果はデフォルト値を含む列挙型ですが、列挙型が空でない場合は、同じ列挙型が返されます(最初の要素だけでなく)。

このメソッドは、列挙可能要素に要素が含まれているかどうかのみをチェックしているように見えるかもしれませんが、そうではありません。

最終的に利回りが2倍になるのはなぜですか?

いいえidead、:)

+1

ありがとうございました。 :-) –

関連する問題