C#のforeachの仕組み(3) IEnumeratorの独自実装コード

[C#のforeachの仕組み(2) forとの比較コード](https://qiita.com/y_ohr/items/a8f50b15657b8b256caf)の続きです。

Niigata.NET 3.0に参加して、forと比較して、foreachをブラックボックスに感じたので、少し整理したい。という話です。
[Niigata.NET 3.0に参加してきました](https://qiita.com/y_ohr/items/e7ef2c22c5da5a675f3f)

# 概要

[前回](https://qiita.com/y_ohr/items/a8f50b15657b8b256caf)、コメントにて@albireoに『ArrayのかわりにListを使えばお手軽に自前Enumeratorを試せますよ。』と、教えてもらったので、早速実装して確認してみました。
結論を言うと、実装面ではモヤモヤがかなりスッキリしました。
ありがとうございました。

# IEnumeratorを独自実装

はじめに、IEnumeratorを独自実装したListの拡張クラスを作成しました。
※余談ですが、netcoreのlist実装が見当たらないのですが、どちらにおいででしょうか・・・？
https://github.com/dotnet/corefx/tree/master/src/System.Collections/src/System/Collections/Generic

``` antlr-csharp:Implement_ListWithLoopConditions
/// <summary>
/// ref https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs
/// BTW, where class list in https://github.com/dotnet/corefx ?
/// </summary>
class ListWithLoopConditions<T> : List<T>
{
    public int? LoopStart { get; set; }
    public int? LoopEnd { get; set; }
    public int? LoopIncrement{ get; set; }

    public new IEnumerator<T> GetEnumerator()
    {
        return new EnumeratorWithLoopConditions(this, LoopStart, LoopEnd, LoopIncrement);
    }

    struct EnumeratorWithLoopConditions : IEnumerator<T>
    {
        private List<T> list;
        private T current;
        private int start;
        private int index;
        private int end;
        private int increment;

        public EnumeratorWithLoopConditions(List<T> list, int? start, int? end, int? increment)
        {
            this.list = list;
            this.current = default(T);
            this.start = start ?? 0;
            this.index = this.start;
            this.end = end ?? list.Count;
            this.increment = increment ?? 1;
        }

        public bool MoveNext()
        {
            if ((start < end && index < end) || (start > end && index > end))
            {
                current = list[index];
                index += increment;
                return true;
            }
            return false;
        }

        public void Reset()
        {
            index = start;
            current = default(T);
        }

        public T Current => current;
        object IEnumerator.Current => current;
        public void Dispose() { }
    }
}
```
# 動作確認

上記の拡張Listを使用して確認してみます。
まず、通常ループの確認。

``` antlr-csharp:Implement_All_loop
class Program
{
    static void Main()
    {
        var numbers = new ListWithLoopConditions<int>() { 0, 1, 2, 3, 4, 5, 6 };

        Console.WriteLine("All loop by for");
        for (int i = 0; i < numbers.Count; i++)
        {
            Console.WriteLine(numbers[i]);
        }

        Console.WriteLine("All loop by foreach with loop conditions");
        foreach (var n in numbers)
        {
            Console.WriteLine(n);
        }
    }
}
```
```:Result_All_loop
All loop by for
0
1
2
3
4
5
6
All loop by foreach with loop conditions
0
1
2
3
4
5
6
```
次に、条件指定付きループの確認。

``` antlr-csharp:Implement_Select_loop
class Program
{
    static void Main()
    {
        var numbers = new ListWithLoopConditions<int>() { 0, 1, 2, 3, 4, 5, 6 };

        Console.WriteLine("Select loop by for");
        for (int i = 1; i < numbers.Count - 1; i += 2)
        {
            Console.WriteLine(numbers[i]);
        }

        Console.WriteLine("Select loop by foreach with loop conditions");
        numbers.LoopStart = 1;
        numbers.LoopEnd = numbers.Count - 1;
        numbers.LoopIncrement = 2;
        foreach (var n in numbers)
        {
            Console.WriteLine(n);
        }
    }
}
```
```:Result_Select_loop
Select loop by for
1
3
5
Select loop by foreach with loop conditions
1
3
5
```
最後に、逆順で条件指定付きループの確認。

``` antlr-csharp:Implement_Reverse_select_loop
class Program
{
    static void Main()
    {
        var numbers = new ListWithLoopConditions<int>() { 0, 1, 2, 3, 4, 5, 6 };

        Console.WriteLine("Reverse select loop by for");
        for (int i = numbers.Count - 1; i > 1; i -= 2)
        {
            Console.WriteLine(numbers[i]);
        }

        Console.WriteLine("Reverse select loop by foreach with loop conditions");
        numbers.LoopStart = numbers.Count - 1;
        numbers.LoopEnd = 1;
        numbers.LoopIncrement = -2;
        foreach (var n in numbers)
        {
            Console.WriteLine(n);
        }
    }
}
```
```:Result_Reverse_Select_loop
Reverse select loop by for
6
4
2
Reverse select loop by foreach with loop conditions
6
4
2
```

# まとめ

- foreachは、列挙された集合の各要素に対して、埋め込み文を実施する。
- 集合がどのように列挙されるかについては、System.Collections.IEnumeratorのMoveNext()にて、要素の列挙方法が制御されている。
- 従って、MoveNext()を独自実装すると、任意の列挙が可能である。
- ゆえに、任意で列挙すると、foreachの対象を制御できるため、結果、ループの振る舞いに介入できる。

# 調べたいこと

- .NetがIEnumeratorを実行する仕組み
- ildasmとDisassembly

# スペシャルサンクス

- @shiracamus
- @albireo
- @masaru_b_cl
- @ozwk

※コメント順