2015-10-05 7 views
10

IEnumerableを繰り返し処理するたびに異なるオブジェクトを生成するファクトリとして使用しようとしています。彼らはできるだけ早くGC'edする必要があります。ただし、は列挙子への参照を保持していますので、再度呼び出すことができます。だから、私たちのプログラムは、基本的に次のようになります。"yield"を使用すると、コンパイラ生成型がIEnumerableとIEnumeratorの両方を実装する理由

public class YieldSpec 
{ 
    public static IEnumerable<string> Strings() 
    { 
     yield return "AAA"; 
     yield return "BBB"; 
     yield return "CCC"; 
    } 
    public void YieldShouldAllowGC() 
    { 
     var e = Strings(); 
     foreach (var a in e) 
     { 
      Console.WriteLine(a); 
     } 

    } 
} 

は、デバッガにそのコードを見てみると:

enter image description here

あなたはブレークポイントにヒットしたときのIEnumerableが「CCCへの参照を持っていることがわかります"

これは実際には起こりません。 IEnumerableは、GetEnumeratorが呼び出されたときにのみIEnumeratorを生成する必要があります。 IEnumerableに状態が含まれることが予想される動作ですか?

+0

私はそれを再現できません。もちろん、すべてのインスタンスを廃棄しましたか? [this code](https://dotnetfiddle.net/M4AdN8)を使用します。 –

+0

インスタンスを「テスト」から削除しましたか?あなたが 'Test'を投げるまで、全体を処理しません。 (それは怠惰です) –

+1

AFAIKは 'Test()'(あるいはもっと具体的には、 'foreach()'の後にスコープを残しているので、列挙子はガベージコレクションの対象になります。何が問題ですか?それは_Directly_ 'Test()'を呼び出した後、GCはまだ起動しませんでしたか? – CodeCaster

答えて

8

パフォーマンス上の理由から、具体的には、ステートマシンはIEnumerableIEnumeratorの両方を実装しています。それは正しく、これを行うには十分スマートです。 IEnumerableにはIEnumeratorが返されるのは初めてのことです。将来、GetEnumeratorを呼び出すと、オブジェクトの新しいインスタンスが作成されるため、別々のイテレーターの状態を維持できます。 IEnumerableを複数作成することが重要ですが、の広範なの実際の状況の大部分は作成されているため、最適化された状況です。

+0

これはデバッガの実行時に見た混乱を明らかにしています。 IEnumerableを複数回実行しましたが、最初の実行時に__Current__プロパティが更新されているだけでした。これはすべてを明確にします。 – bradgonesurfing

+2

@bradgonesurfing:この正解を明確にするため、ここでの「パフォーマンスの理由」は* GCの圧力を下げる*です。 'yield'を含むこれらのメソッドが1秒間に数千回呼び出され、直ちに列挙される状況を簡単に想像することができます。それが発生したときに作成される新しいオブジェクトの数の半分を減らすことができれば、それは明らかな勝利です。 –

+1

こんにちは@EricLippert私は完全に推論を理解し、それは完全なトレードオフのようです。それは私がGC'dされていたはずだったと仮定したプロファイリングを通してオブジェクトの全負荷を見つけたので、私はただ私を立ち上げました。私の場合は、WPFコンテキストメニューの大きな階層のルートノードです。私は彼らがIEnumerableによって保持される代わりにGC'dを取得することを好むだろう。私のケースは外れ値であり、私はそれを回避する方法を見つけるでしょう。 – bradgonesurfing

関連する問題