LoginSignup
0
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-11-23

C#のforeachの仕組み(2) forとの比較コードの続きです。

Niigata.NET 3.0に参加して、forと比較して、foreachをブラックボックスに感じたので、少し整理したい。という話です。
Niigata.NET 3.0に参加してきました

概要

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

IEnumeratorを独自実装

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

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を使用して確認してみます。
まず、通常ループの確認。

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

次に、条件指定付きループの確認。

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

最後に、逆順で条件指定付きループの確認。

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

スペシャルサンクス

※コメント順

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0