c嗳重ね着と列挙器を深く理解する。


皆さん、こんにちは、これは「C铉.NET拾遺漏補完」シリーズの07編の文章です。
C〓〓の中で、大多数の方法はすべてreturn文を通じて(通って)直ちにプログラムのコントロールを调节者に手渡すので、同时に方法の内の当地の资源をも放してしまうことができます。一方、yield文を含む方法は、順番に複数の値を返して調整者に与えた期間にローカルリソースを保持し、すべての値が戻ってきたら元のリソースを解放し、これらの戻り値は一連のシーケンスを形成して使用者によって使用される。C〓の中で、このようなyieldの語句を含む方法、属性あるいは索引はシーズマリーです。
ディケンサのyield文は2つに分けられます。
  • yeild returnは、プログラム制御権をコーディネーターに戻してローカルの状態を保持し、コーディネーターが戻ってきた値を取って後で実行します。
  • yeild breakは、プログラムの現在のシーケンスが終了していることを知らせるために、通常のコードブロックに相当するreturn文(ディケンサで直接にreturnを使用するのは違法です)。
  • 
    IEnumerable<int> Fibonacci(int count)
    {
     int prev = 1;
     int curr = 1;
     for (int i = 0; i < count; i++)
     {
     yield return prev;
     int temp = prev + curr;
     prev = curr;
     curr = temp;
     }
    }
    
    void Main()
    {
     foreach (int term in Fibonacci(10))
     {
     Console.WriteLine(term);
     }
    }
    出力:
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    実際のシーンでは、私たちは普通、直接的にはあまり書きません。反復が必要なシーンの多くは配列、セット、リストです。これらのタイプの内部はすでに必要なローズマリーをカプセル化しました。例えばC〓〓の配列がトラバースされるのは、IEnumerableインターフェースが実現されたためであり、Get Enumerator()法により配列の列挙器Enumeratorが得られます。たとえば、最も一般的な使用シーンは、次のように配列要素を1つずつ印刷する配列の例である。
    
    int[] numbers = { 1, 2, 3, 4, 5 };
    IEnumerator enumerator = numbers.GetEnumerator();
    while (enumerator.MoveNext())
    {
     Console.WriteLine(enumerator.Current);
    }
    実はこれがforeachの作業原理です。上のコードはforeachで次のように書き換えられます。
    
    int[] numbers = { 1, 2, 3, 4, 5 };
    foreach (int number in numbers)
    {
     Console.WriteLine(number);
    }
    もちろん、列挙器は必ずしもローズマリーによって実現されなくてもいいです。例えば、以下のカスタムの列挙器Coffee Enumeratorです。
    
    public class CoffeeCollection : IEnumerable
    {
     private CoffeeEnumerator enumerator;
     public CoffeeCollection()
     {
     enumerator = new CoffeeEnumerator();
     }
    
     public IEnumerator GetEnumerator()
     {
     return enumerator;
     }
    
     public class CoffeeEnumerator : IEnumerator
     {
     string[] items = new string[3] { "espresso", "macchiato", "latte" };
     int currentIndex = -1;
     public object Current
     {
     get
     {
     return items[currentIndex];
     }
     }
     public bool MoveNext()
     {
     currentIndex++;
     if (currentIndex < items.Length)
     {
     return true;
     }
     return false;
     }
     public void Reset()
     {
     currentIndex = 0;
     }
     }
    }
    使用:
    
    public static void Main(string[] args)
    {
     foreach (var coffee in new CoffeeCollection())
     {
     Console.WriteLine(coffee);
     }
    }
    ディケンサと列挙器を理解することで、より効率的なコードを作成することができます。例えば一つを判断する IEnumerable オブジェクトに元素が含まれていますか?よく見られます。
    
    if(enumerable.Count() > 0)
    {
     //       
    }
    ただし、列挙器の思考で少し考えれば分かります。Count() 集合要素の数を得るためには、必ずすべての要素が反復され、時間の複雑さはO(n)である。セットに要素が含まれているかどうかを知るだけで、実は反復すればいいです。だから効率のいいやり方は:
    
    if(enumerable.GetEnumerator().MoveNext())
    {
     //       
    }
    このように書く時間の複雑さはO(1)で,効率は明らかにより高い。書くのが便利なために、C〓は拡張方法を提供しました。 アンy()
    
    if(enumerable.Any())
    {
     //       
    }
    必要があれば、できるだけAny方法を使って、効率がいいです。
    また、例えばEF Coreでは、実行が必要です。  IQueryable<T> お問い合わせの際に、時々使うことがあります。  AsEnumerable() ToList、ToArayなどを使うより効率的です。ToList、ToArayなどはすぐに列挙操作を行います。 As Enumerable() 列挙操作を本当に必要な時に延期して実行してもいいです。もちろん、実際の応用シーンも考慮して、Aray、Listなどは調整者に使いやすく、特に元素の総数量、添削要素などの操作を取得します。
    以上はcylaのディテールと列挙器の詳細を深く理解しています。cylaのディテールと列挙器に関する資料は他の関連記事に注目してください。