程序笔记   发布时间:2022-07-04  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了07 迭代器与协程大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

什么是迭代器?

在使用迭代器之前,我们如何遍历一个结构?

很简单,用一个for循环就可以遍历了

for(int i = 0;i< nums.Count; i++)
{
    Debug.Log(nums[i]);
}

很自然对不对?但是,虑两个问题:

  1. 为什么要从0开始遍历?
  2. 为什么每次是+1?

这两个问题,看起来莫名其妙,但是很关键。现在,数组是我们的遍历对象,数组是如何定义的,其实我们是不知道的(下标从0开始只是一种约定)

此时的遍历逻辑,完全是我们外部定义的, 是我们自己写的,而非数组本身提供的,这并不符合设计的初衷

虑一下,如果要用for遍历一个字典,怎么办?没有办法,因为我们完全不知道内部结构,没法给出遍历逻辑

迭代器模式

迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示

在C#中,继承自IEnumerable或者IEnumerator的类可以认为是一个可以迭代的类

IEnumerator

迭代器,继承自此接口的类,需要提供迭代方法

public class students : IEnumerator
{
    privatE int index;
        
    private String id;
    private String name;
    private String score ;
        
    public bool MoveNext()
    {
        if (index < 3)
            return true;
        return false;
    }

    public void Reset()
    {
        throw new System.NotImplementedException();
    }

    public object Current
    {
        get
        {
            switch (indeX)
            {
                case 0:
                    index++;
                    return id;
                case 1:
                    index++;
                    return name;
                case 2:
                    index++;
                    return score;
                default:
                    return null;
            }
        }
    }

    public students(String id, String name, String score)
    {
        this.id = id;
        this.name = name;
        this.score = score;
    }
        
}
public class CoroutIne : MonoBehaviour
{
    private void Start()
    {
        students s = new students("1", "A", "90");
        while (s.MoveNext())
        {
            Debug.Log(s.Current);
        }
    }
}

07 迭代器与协程

很明显,只要继承了迭代器接口,我们就能够用MoveNext以及Current来访问一个集合了,此时,集合的遍历逻辑是定义在类内部的,我们不需要知道他的实现,也不需要自己定义遍历逻辑

但此时还不支持使用foreach进行遍历,我们是手动通过while进行的遍历

IEnumerable

意为可迭代的,基础自此接口的类,需要返回一个迭代器

public class students : IEnumerable, IEnumerator
{
    privatE int index;

    private String id;
    private String name;
    private String score;

    public bool MoveNext()
    {
        if (index < 3)
            return true;
        return false;
    }

    public void Reset()
    {
        throw new System.NotImplementedException();
    }

    public object Current
    {
        get
        {
            switch (indeX)
            {
                case 0:
                    index++;
                    return id;
                case 1:
                    index++;
                    return name;
                case 2:
                    index++;
                    return score;
                default:
                    return null;
            }
        }
    }

    public students()
    {
    }

    private students(String id, String name, String score)
    {
        this.id = id;
        this.name = name;
        this.score = score;
    }

    public IEnumerator getEnumerator()
    {
        return new students("123", "AAA", "99");
    }
}
public class CoroutIne : MonoBehaviour
{
    private void Start()
    {
        students s = new students();
        foreach (var student in s)
        {
            Debug.Log(student);
        }
    }
}

07 迭代器与协程

通过断点,我们可以彻底搞明白,这个foreach到底对IEnumerable做了什么

  1. 最开始,先进入in,调用获取可迭代对象的方法,获取一个new student()

    07 迭代器与协程

07 迭代器与协程

  1. 随后调用MoveNext方法,判断迭代器是否可以进行迭代

    07 迭代器与协程

  2. 如果迭代器可以进行迭代,那么再从Current方法中获取当前值

    07 迭代器与协程

那结果很明显了,foreach就是一个语法糖,编译器会把foreach编程成类似如下结构

var enumerator = collection.GetEnumerator();
while (enumerator.MoveNext()) 
{
    var item = enumerator.Current;
    {
        // foreach中的操作
    }
}

yield return

通过上面两个案例,很清楚什么是迭代器了,但是,我想说,创建这么一个类真的好麻烦...

于是又出现另一个语法糖yield

我们重新定义一下上面的方法

public class CoroutIne : MonoBehaviour
{
    private void Start()
    {
        var test = Test();
        while (test.MoveNext())
        {
            Debug.Log(test.Current);
        }
    }

    private IEnumerator Test()
    {
        Debug.Log("AAAA");
        yield return 1;
        Debug.Log("bbbb");
        Debug.Log("CCCC");
        yield return 2;
        Debug.Log("DDDD");
        yield return 3;
    }
}

07 迭代器与协程

如果你试图从一个常规的函数角度来思这个Test方法,那一定会非常,非常,非常困惑...

事实上,这是一个语法糖,Test并不是一个常规函数,编译器会为我们做好许多其他的操作...

我们查看一下反编译代码,会发现编译器确实帮我们生成了一个Test类

07 迭代器与协程

而Test类也确实继承了IEnumerator

[CompilerGenerated]
private sealed class <Test>d__1 : IEnumerator<object>, IEnumerator, IDisposable
{
	//...
}

此时再查看我们调用Test方法的那一行

private void Start()
{
	IEnumerator test;
	test = this.Test();
	while (test.MoveNext())
	{
		Debug.Log(test.Current);
	}
}

发现没?就是实例化了一个Test类而已

我们再来仔细研究一下编译器帮助我们生成的这个类

[CompilerGenerated]
private sealed class <Test>d__1 : IEnumerator<object>, IEnumerator, IDisposable
{
	privatE int <>1__state;

	private object <>2__current;

	public CoroutIne <>4__this;
    
    // ...
}

首先是构造方法

[DebuggerHidden]
public <Test>d__1(int <>1__statE)
{
	this.<>1__state = <>1__state;
}

就是给状态赋值,但事实上在上面的图中可以发现,实例化这个类的时候是没有任何参数传入的,所以默认的<>1__state就是0

最关键的MoveNext方法

private bool MoveNext()
{
	switch (this.<>1__statE)
	{
		default:
			return false;
		case 0:
			this.<>1__state = -1;
			Debug.Log((object)"AAAA");
			this.<>2__current = 1;
			this.<>1__state = 1;
			return true;
		case 1:
			this.<>1__state = -1;
			Debug.Log((object)"bbbb");
			Debug.Log((object)"CCCC");
			this.<>2__current = 2;
			this.<>1__state = 2;
			return true;
		case 2:
			this.<>1__state = -1;
			Debug.Log((object)"DDDD");
			this.<>2__current = 3;
			this.<>1__state = 3;
			return true;
		case 3:
			this.<>1__state = -1;
			return false;
	}
}

default我们不管

首先会让state=-1,大概是一种保护作用吧

随后调用迭代器至yield return(包括)之间的所有方法,并且yield return的返回值就是current

然后让当前状态+1

如果我们不调用movenext,那current就会一直保持上一次的结果,如果我们一次movenxet都没有使用过,那么current会是Null

协程

首先,说在开头,协程,他就是一个迭代器的扩展!

用过协程的都知道,协程可以在每一帧进行一次迭代,而不是一次性就迭代完,这和我们上面写的迭代器可能有所不同

因为我们只有一个迭代器...用的还是while一次性迭代到死,那必然是不可能实现协程这种分帧指行的能力...

事实上,在我们上面的例子中,迭代器和while循环没有什么不同...

此时,我们还需要一个协程管理类!

当然在此之前,我们再虑一下,Unity的协程是可以返回很多类型的,比如Null啊,WaitForSecond啊,这里Unity的封装肯定比我复杂,我们简单提供一下示例

先定义一个接口用于等待

public interface IWait
{
    bool Tick();
}

实现两个等待类型

public class WaitForSeconds : IWait
{
    private float waitTime;

    public WaitForSeconds(float waitTimE)
    {
        this.waitTime = waitTime;
    }

    public bool Tick()
    {
        waitTime -= Time.deltaTime;
        return waitTime <= 0;
    }
}

public class WaitForFrames : IWait
{
    privatE int waitFrame;

    public WaitForFrames(int waitFramE)
    {
        this.waitFrame = waitFrame;
    }
        
    public bool Tick()
    {
        waitFrame -= 1;
        return waitFrame <= 0;
    }
}

协程管理器

public class CoroutIneManager
{
    private LinkedList<IEnumerator> coroutInes = new LinkedList<IEnumerator>();

    public void StartCoroutIne(IEnumerator iE)
    {
        coroutInes.AddLast(iE);
    }

    public void Onupdate()
    {
        var node = coroutInes.First;
        while (node != null)
        {
            var ie = node.Value;
            var flag = true;
                
            // 如果是等待类型
            if (ie.Current is IWait)
            {
                var wait = ie.Current as IWait;
                if (wait!=null && wait.Tick())
                {
                    flag = ie.MoveNext();
                }
            }
            // null类型
            else if (ie.Current is null) 
            {
                flag = ie.MoveNext();
            }
            else
            {
                flag = ie.MoveNext();
            }

            if (!flag)
            {
                coroutInes.Remove(nodE);
            }
            node = node.Next;
        }
    }
}

其实很简单,我们只是遍历了当前所有的迭代器,并对每一个迭代器的状态进行判断而已

再提醒一下,MoveNext会调用迭代器到下一次yield return(包括)之间的所有方法,而yield return的返回值就是Current

此时在主工程中调用即可

public class CoroutIne : MonoBehaviour
{
    private CoroutIneManager manager;
    private void Start()
    {
        manager = new CoroutIneManager();
        manager.StartCoroutIne(Test());
    }

    private IEnumerator Test()
    {
        Debug.Log(1);
        yield return new WaitForSeconds(1);
        Debug.Log(2);
        yield return new WaitForFrames(20);
        Debug.Log(3);
    }

    private void update()
    {
        manager.onupdate();
    }
}

07 迭代器与协程

大佬总结

以上是大佬教程为你收集整理的07 迭代器与协程全部内容,希望文章能够帮你解决07 迭代器与协程所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。