イテレーターの仕組み

イテレーター(反復)動作についてC#では、仕組みが用意されています。
ユーザーは、簡易に使えるように内部で処理をされていますが、今回は、どのようにコードがコンパイラで展開されているのかをみてみましょう

yield returnを2行のみ

ユーザー作成コード

using System;
using System.Collections.Generic;

namespace YieldTest
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (var item in Collection())
            {
                Console.WriteLine(item);
            }
        }

        private static IEnumerable<object> Collection()
        {
            yield return 1;
            yield return 2;
        }
    }
}

実行結果

1
2

コンパイラが展開したコード

複雑にはなっていますが、状態管理(ステート管理)を行い、今どのフェーズか(実行中?終了?)を都度管理しているのがわかります。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;

namespace YieldTest
{
    internal class Program
    {
        [CompilerGenerated]
        private sealed class MyClass : IEnumerable<object>, IEnumerable, IEnumerator<object>, IDisposable, IEnumerator
        {
            private int state;

            private object current;

            private int initialThreadId;

            object IEnumerator<object>.Current
            {
                get
                {
                    return current;
                }
            }

            object IEnumerator.Current
            {
                get
                {
                    return current;
                }
            }

            public MyClass(int state)
            {
                this.state = state;
                initialThreadId = Environment.CurrentManagedThreadId;
            }

            void IDisposable.Dispose()
            {
            }

            private bool MoveNext()
            {
                switch (state)
                {
                    default:
                        return false;
                    case 0:
                        current = 1;
                        state = 1;
                        return true;
                    case 1:
                        current = 2;
                        state = 2;
                        return true;
                    case 2:
                        state = -1;
                        return false;
                }
            }

            bool IEnumerator.MoveNext()
            {
                //ILSpy generated this explicit interface implementation from .override directive in MoveNext
                return this.MoveNext();
            }

            void IEnumerator.Reset()
            {
                throw new NotSupportedException();
            }

            IEnumerator<object> IEnumerable<object>.GetEnumerator()
            {
                if (state == -2 && initialThreadId == Environment.CurrentManagedThreadId)
                {
                    state = 0;
                    return this;
                }
                return new MyClass(0);
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return ((IEnumerable<object>)this).GetEnumerator();
            }
        }

        private static void Main(string[] args)
        {
            IEnumerator<object> enumerator = Collection().GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    object current = enumerator.Current;
                    Console.WriteLine(current);
                }
            }
            finally
            {
                if (enumerator != null)
                {
                    enumerator.Dispose();
                }
            }
        }

        private static IEnumerable<object> Collection()
        {
            return new MyClass(-2);
        }
    }
}

yield returnをfor文で繰り返す

ユーザー作成コード

using System;
using System.Collections.Generic;

namespace YieldTest
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (var item in Collection())
            {
                Console.WriteLine(item);
            }
        }

        private static IEnumerable<object> Collection()
        {
            for (int i = 0; i < 10; i++)
            {
                yield return i;
            }
        }
    }
}

実行結果

0
1
2
3
4
5
6
7
8
9

コンパイラが展開したコード

複雑にはなっていますが、状態管理(ステート管理)を行い、今どのフェーズか(実行中?終了?)を都度管理しているのがわかります。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace YieldTest
{
    internal class Program
    {
        private sealed class MyClass : IEnumerable<object>, IEnumerable, IEnumerator<object>, IDisposable, IEnumerator
        {
            private int state;

            private object current;

            private int initialThreadId;

            private int next;

            object IEnumerator<object>.Current
            {
                get
                {
                    return current;
                }
            }

            object IEnumerator.Current
            {
                get
                {
                    return current;
                }
            }

            public MyClass(int state)
            {
                this.state = state;

                initialThreadId = Environment.CurrentManagedThreadId;
            }

            void IDisposable.Dispose()
            {
            }

            private bool MoveNext()
            {
                int num = state;
                if (num != 0)
                {
                    if (num != 1)
                    {
                        return false;
                    }
                    state = -1;
                    next++;
                }
                else
                {
                    state = -1;
                    next = 0;
                }
                if (next < 10)
                {
                    current = next;
                    state = 1;
                    return true;
                }
                return false;
            }

            bool IEnumerator.MoveNext()
            {
                //ILSpy generated this explicit interface implementation from .override directive in MoveNext
                return this.MoveNext();
            }

            void IEnumerator.Reset()
            {
                throw new NotSupportedException();
            }

            IEnumerator<object> IEnumerable<object>.GetEnumerator()
            {
                if (state == -2 && initialThreadId == Environment.CurrentManagedThreadId)
                {
                    state = 0;
                    return this;
                }
                return new MyClass(0);
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return ((IEnumerable<object>)this).GetEnumerator();
            }
        }

        private static void Main(string[] args)
        {
            IEnumerator<object> enumerator = Collection().GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    object current = enumerator.Current;
                    Console.WriteLine(current);
                }
            }
            finally
            {
                if (enumerator != null)
                {
                    enumerator.Dispose();
                }
            }
        }

        private static IEnumerable<object> Collection()
        {
            return new MyClass(-2);
        }
    }
}

C#,Unity

Posted by hidepon