从c#迭代器到unity协程

先从c# 1.0的迭代器说起,这里主要借鉴《C# in depth》里对于迭代器内容的案例,并加以详细的说明

对一个对象要执行foreach遍历操作,这个对象必须继承Ienumbable接口,并实现GetIenumator方法,并且返回一个实现了IEnumerator接口的实例,

class Program
    {
        static void Main(string[] args)
        {
            object[] values = { "a", "b", "c", "d" };
            IterationSample collection = new IterationSample(values, 1);
            foreach (var item in collection)
            {
                Console.WriteLine(item);
            }
        }
    }
    public class IterationSample : IEnumerable
    {
        object[] values;
        int startingPoint;
        // 构造函数
        public IterationSample(object[] values, int startingPoint)
        {
            this.values = values;
            this.startingPoint = startingPoint;
        }
        // IEnumaerable接口实现
        public IEnumerator GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }

接下来,我们使用c#1.0的方式实现这个GetEnumerator方法,来实现对IterationSample中数据的遍历,要实现GetEnumerator方法,需要注意以下两点

1.需要返回实现了IEnumerator接口的实例,我们采用创建内嵌类来实现,嵌套类型是可以访问它外层类型的私有成员, c# in depth中有解释为什么不在IterationSample去直接实现IEnumerator接口(也可以自行发散一下思维)

2.创建的内嵌类中需要存储指向“父级” (当前例子即指向IterationSample)以及当前遍历位置

下面声明IterationSampleIterator作为IterationSample的内嵌类累实现IEnumerator接口

 

public class IterationSampleIterator : IEnumerator
        {
            IterationSample parent;
            int position;
            internal IterationSampleIterator(IterationSample parent)
            {
                this.parent = parent;
                position = -1;    //在第一个元素之前开始,
            }
//接口属性实现,返回当前值
public object Current { get { if (position == -1 || position == parent.values.Length) { throw new InvalidOperationException(); } int index = position + parent.startingPoint; index = index % parent.values.Length; return parent.values[index]; } }
//接口方法实现,每次要获取值之前,调用MoveNext方法,返回false则遍历结束
public bool MoveNext() { //如果需要遍历,则增加position值 if(position!=parent.values.Length) { position++; } //当不可遍历时,返回false return position < parent.values.Length; } public void Reset() { position = -1; } }

实现了迭代器类,接下里修改一下IterationSample中的GetIEnumerator方法,就可以实现对IterationSample类中的valuse数组进行遍历了

   // IEnumaerable接口实现
        public IEnumerator GetEnumerator()
        {
//返回迭代器类的实例,当我们使用foreach循环时,会自动调用迭代器类中的MoveNext方法并获取current,直到MoveNext返回false
return new IterationSampleIterator(this); }

我们经常可能也会看见迭代的另一种写法

               IEnumerable iterable = new IterationSample(values, 1);
                IEnumerator iterator = iterable.GetEnumerator();
                while (iterator.MoveNext())
                {
                    Console.WriteLine(iterator.Current);
                }

正因为在c#1中实现迭代器很麻烦,所以在c#2中提供了yield关键字来简化迭代器的实现,在c#2中,我们只需几行简单的代码 就可以完全代替上面在IterationSample中实现的IterationSampleIterator内嵌类的功能,使用yield关键字改写GetIEnumator方法,只需两行代码

public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < values.Length ; i++)
                yield return values[(i + startingPoint) % values.Length];
        }

我们使用.Reflector反编译这段代码来查看编译器都做了哪些工作,进一步的理解yield语句,反编译后的代码如下

 

从上面的C#实现可以知道:函数内有多少个 yield return 在对应的 MoveNext() 就会返回多少次 true (不包含嵌套)。另外非常重要的一点的是:同一个函数内的其他代码(不是 yield return 语句)会被移到 MoveNext 中去。

 有了yield关键字,我们还可以更方便的实现自定义的遍历函数,比如我们在IterationSample中添加一个MySort方法,返回类型为实现IEnumerable接口的实例,因为使用foreach,遍历的集合必须是实现IEnumerable接口的实例,因为有yield关键字,所以编译后Mysort方法中返回的实例对象即就是实现IEnumerable接口的父类实例

 public IEnumerable MySort()
        {
          //我们设置只遍历前2个元素
            for (int i = 0; i < values.Length-2; i++)
                yield return values[(i + startingPoint) % values.Length];

        }

 接下来使用我们自定义的方法来遍历

            IterationSample collection = new IterationSample(values, 1);
            foreach (var item in collection.MySort() )
            {
                Console.WriteLine(item);
            }

关于yield语句的一些使用注意事项,这里就不在讲,只说明yield语句背后的工作原理,可以自行了解,c# in depth中也有详细说明

接下里了解一下yield语句在unity协程中的使用 

 

c# 2.0 yield关键字对迭代器的封装

反编译yield关键字实现的内容

unity协程的几种用法 及释义

嵌套线程的执行顺序

posted @ 2018-05-24 21:36  昵称起半天的人  阅读(280)  评论(0编辑  收藏  举报