从foreach 到yield 来聊聊协程

C#实现foreach  其实是通过对一个集合不断的迭代输出的过程 。  它需要满足的条件是

 

 1 foreach的对象有 GetEnumarator方法 来返回迭代对象的集合

 2 需要有一个类继承IEnumerator接口。该类有一个集合保存了所有需要迭代的对象,并且该类中必须有几个方法:返回一个是否还有需要迭代的对象的bool MoveNext()方法,重新设置迭代索引的 Reset()方法,还有返回当前迭代对象的IEnumerator方法。以List的底层实现为例子

 

下面我们自己实现一个功能来巩固一下:

  public class IteratorTarget 
    {
        public IteratorTarget(string fName, string lName)
        {
            this.firstName = fName;
            this.lastName = lName;
        }


        public string firstName;
        public string lastName;

    }

 

public class ForeachObject
    {
        private IteratorTarget[] iteratorTargetArray;
        public ForeachObject(IteratorTarget[] pArray)
        {
            iteratorTargetArray = new IteratorTarget[pArray.Length];


            for (int i = 0; i < pArray.Length; i++)
            {
                iteratorTargetArray[i] = pArray[i];
            }
        }

      public IteratorObject GetEnumerator()
        {
            return new IteratorObject(iteratorTargetArray);
        }

    }

 

 // When you implement IEnumerable, you must also implement IEnumerator.

    public class IteratorObject : IEnumerator
    {
        public IteratorTarget[] IteratorTargets;


        // Enumerators are positioned before the first element
        // until the first MoveNext() call.
        int position = -1;


        public IteratorObject(IteratorTarget[] list)
        {
            IteratorTargets = list;
        }


        public bool MoveNext()
        {
            position++;
            return (position < IteratorTargets.Length);
        }


        public void Reset()
        {
            position = -1;
        }


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


        public IteratorTarget Current
        {
            get
            {
                try
                {
                    return IteratorTargets[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }

    }

上面我们定义了三个类一个迭代的对象IteratorTarget ,一个 foreach的对象 ForeachObject 最后一个 用来继承IEnumerator的类IteratorObject。下面

     static void Main(string[] args)
        {
            IteratorTarget[] IteratorTargetArray = new IteratorTarget[3]
            {
                 new IteratorTarget("John", "Smith"),
                 new IteratorTarget("Jim", "Johnson"),
                 new IteratorTarget("Sue", "Rabon"),
            };
            ForeachObject foreachObject= new ForeachObject(IteratorTargetArray);
                 foreach (IteratorTarget p in foreachObject)
                     Console.WriteLine(p.firstName + " " + p.lastName);
                 Console.Read();

 

输出了

 John Smith

 Jim Johnson

 Sue Rabon

自定义的foreach输出成功!!!

下面该聊聊yield了 先上一个用 yield实现同上面一样功能

 public class Test
    {
        static IteratorTarget[] iteratorTargetArray = new IteratorTarget[3]
        {
                new IteratorTarget("John", "Smith"),
                new IteratorTarget("Jim", "Johnson"),
                new IteratorTarget("Sue", "Rabon"),
        };
            public IEnumerator<IteratorTarget> GetEnumerator()
        {
            foreach (var item in iteratorTargetArray)
            {
                yield return item;
            }
        }

    }

 Main方法变成

  static void Main(string[] args)
        {
              Test test = new Test();
              foreach (var item in test)
              {
                  Console.WriteLine(item.firstName + " " + item.lastName);
              }
              Console.Read();
       }

同样输出了

 

 John Smith

 Jim Johnson

 Sue Rabon

到这里我们应该明白了,其实yield就相当于为我们省去了 创建上面第二个条件的继承自IEnumarator的类。yield的作用就是不断的执行 MoveNetxt(). GetCurrent() 等方法来一步步迭代,其实上面的Test类我们还可以这样写

public class Test
    {
        public IEnumerator<IteratorTarget> GetEnumerator()
        {
            yield return new IteratorTarget("John", "Smith");
            yield return new IteratorTarget"Jim", "Johnson");
            yield return new  IteratorTarget("Sue", "Rabon"),
        }

}

了解了 Yield的具体用法后也就不难理解unity 中协程的作用了,当unity开始一段协程时候,StartCroutine(SomeFunc) SomeFunc是一个返回值为IEnumarator的方法。当迭代器方法运行到 yield return 语句时,会返回一个expression表达式并保留当前在代码中的位置。 当下次调用迭代器函数时执行从该位置重新启动。

        Unity在每帧的Update之后执行协程,做的工作就是:调用 协程(迭代器)MoveNext() 方法,如果返回 true ,就从当前位置继续往下执行从而每帧迭代下去。(插播 unity获得wake start update 协程等方法时候是通过在编译阶段可以是ilcpp或者mono把updae wake 等方法放到不同的list里面然后再运行的时候依次执行,而不是通过运行时候反射拿到 。具体如下

当第一次访问给定类型的单行为时,将通过脚本运行时(Mono或IL2CPP)检查底层脚本是否定义了任何神奇的方法(指的是wake update 等方法),并缓存这些信息。如果一个单值行为有一个特定的方法,它将被添加到一个适当的列表中,例如,如果一个脚本定义了更新方法,那么它将被添加到一个脚本列表中,这些脚本需要在每一帧中进行更新。在游戏中,Unity只是遍历这些列表并执行它的方法)

另外加上我实现的 unity中的协程的 yield return null 功能 。

地址https://github.com/paridas0813/Unity_My_Crotine

Reference: http://www.cnblogs.com/zhaopei/p/5769782.html 

                    http://dsqiu.iteye.com/blog/2029701

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值