{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/",
  "atlas": "https://www.pystone.net/?node=unity-enumerator-coroutine-internals#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals.md",
  "context": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:unity-enumerator-coroutine-internals",
  "slug": "unity-enumerator-coroutine-internals",
  "title": "从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "从Enumerator到StartCoroutine底层分析 概述",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "updatedAt": "2021-03-24T08:57:26Z",
  "contentHash": "a844dd57476ae541380f8f8cdeebd182a97b44b7bb9853a4bb88c66fe448e454",
  "assets": [
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615789031828.png",
      "url": "/media/954ebd3dbd5ec1b1753c.png",
      "mediaType": "image/png",
      "contentHash": "954ebd3dbd5ec1b1753c22c992cd2bd9fe5dd067119bb5fd0ac0bac4884aab6b",
      "byteLength": 172521,
      "width": 1256,
      "height": 664
    },
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615789060736.png",
      "url": "/media/7e38dbbf7cbc82e852b7.png",
      "mediaType": "image/png",
      "contentHash": "7e38dbbf7cbc82e852b7fdda2572ae6fe3807e2223642908408ac925ef89389a",
      "byteLength": 5844,
      "width": 280,
      "height": 88
    },
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615789081096.png",
      "url": "/media/733685674a9561953ba0.png",
      "mediaType": "image/png",
      "contentHash": "733685674a9561953ba0ad94191a71dd03ce1b2576cb16afb6bac495c0798128",
      "byteLength": 14360,
      "width": 490,
      "height": 133
    },
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615789423446.png",
      "url": "/media/93465b3e4ca5133a58a4.png",
      "mediaType": "image/png",
      "contentHash": "93465b3e4ca5133a58a424728a5fac106bdd674a5124557ad771bcfce2b3ad1a",
      "byteLength": 96727,
      "width": 608,
      "height": 619
    },
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615790017077.png",
      "url": "/media/b56492dbf0bb31973f8c.png",
      "mediaType": "image/png",
      "contentHash": "b56492dbf0bb31973f8c164d060b42cb9a0da4595b84125abfcff26ed1a6fd5a",
      "byteLength": 4759,
      "width": 528,
      "height": 49
    },
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615790081591.png",
      "url": "/media/90d652ad1ced619f1e95.png",
      "mediaType": "image/png",
      "contentHash": "90d652ad1ced619f1e9547445d893ae8b09df7fd71fb512a704317a814688834",
      "byteLength": 22048,
      "width": 416,
      "height": 162
    },
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615790157682.png",
      "url": "/media/c73ce42e7b5a8888b050.png",
      "mediaType": "image/png",
      "contentHash": "c73ce42e7b5a8888b050748646ac9a063579e4baa1a217952ab0c9019461903c",
      "byteLength": 24477,
      "width": 557,
      "height": 191
    },
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615794880255.png",
      "url": "/media/8551d86de82b2259016a.png",
      "mediaType": "image/png",
      "contentHash": "8551d86de82b2259016a4de8a560131e325382da3872e0f7efe350d1541b6746",
      "byteLength": 24974,
      "width": 674,
      "height": 110
    },
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615794868354.png",
      "url": "/media/1f14c1a8d9e90d155df5.png",
      "mediaType": "image/png",
      "contentHash": "1f14c1a8d9e90d155df562b0f2ce6ca64d0bc76d7064fcdc9e3d725bcc8f1118",
      "byteLength": 7750,
      "width": 341,
      "height": 62
    },
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615795022810.png",
      "url": "/media/7d699fef64c0859c7dc4.png",
      "mediaType": "image/png",
      "contentHash": "7d699fef64c0859c7dc4b02abb4a39c4354901c952fca86bc165def7b43dccc7",
      "byteLength": 148173,
      "width": 1035,
      "height": 653
    },
    {
      "reference": "assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615796287192.png",
      "url": "/media/1f64bbe3967010bc614b.png",
      "mediaType": "image/png",
      "contentHash": "1f64bbe3967010bc614b2890250df4af4f041619e2f40600be4958866042d16e",
      "byteLength": 23164,
      "width": 422,
      "height": 292
    }
  ],
  "headings": [
    {
      "depth": 1,
      "text": "从Enumerator到StartCoroutine底层分析",
      "anchor": "从enumerator到startcoroutine底层分析",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#%E4%BB%8Eenumerator%E5%88%B0startcoroutine%E5%BA%95%E5%B1%82%E5%88%86%E6%9E%90"
    },
    {
      "depth": 2,
      "text": "概述",
      "anchor": "概述",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#%E6%A6%82%E8%BF%B0"
    },
    {
      "depth": 2,
      "text": "迭代器模式的实现与Enumerator",
      "anchor": "迭代器模式的实现与enumerator",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%AE%9E%E7%8E%B0%E4%B8%8Eenumerator"
    },
    {
      "depth": 3,
      "text": "foreach的原理",
      "anchor": "foreach的原理",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#foreach%E7%9A%84%E5%8E%9F%E7%90%86"
    },
    {
      "depth": 3,
      "text": "迭代器模式的实现",
      "anchor": "迭代器模式的实现",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%AE%9E%E7%8E%B0"
    },
    {
      "depth": 3,
      "text": "自己实现一个List",
      "anchor": "自己实现一个list",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#%E8%87%AA%E5%B7%B1%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AAlist"
    },
    {
      "depth": 2,
      "text": "Enumerator与yield return",
      "anchor": "enumerator与yield-return",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#enumerator%E4%B8%8Eyield-return"
    },
    {
      "depth": 2,
      "text": "Unity3D协程",
      "anchor": "unity3d协程",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#unity3d%E5%8D%8F%E7%A8%8B"
    },
    {
      "depth": 3,
      "text": "本质是什么",
      "anchor": "本质是什么",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#%E6%9C%AC%E8%B4%A8%E6%98%AF%E4%BB%80%E4%B9%88"
    },
    {
      "depth": 3,
      "text": "yield return 的一些特殊含义",
      "anchor": "yield-return-的一些特殊含义",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#yield-return-%E7%9A%84%E4%B8%80%E4%BA%9B%E7%89%B9%E6%AE%8A%E5%90%AB%E4%B9%89"
    },
    {
      "depth": 3,
      "text": "一些Demo",
      "anchor": "一些demo",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#%E4%B8%80%E4%BA%9Bdemo"
    },
    {
      "depth": 3,
      "text": "AssetBundle异步加载",
      "anchor": "assetbundle异步加载",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#assetbundle%E5%BC%82%E6%AD%A5%E5%8A%A0%E8%BD%BD"
    },
    {
      "depth": 3,
      "text": "UnityWebRequest",
      "anchor": "unitywebrequest",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#unitywebrequest"
    },
    {
      "depth": 2,
      "text": "Ref:",
      "anchor": "ref",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#ref"
    },
    {
      "depth": 2,
      "text": "从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？",
      "anchor": "从enumerator到startcoroutine底层分析容器的概念枚举器与代码的协程异步执行究竟有什么关系",
      "citation": "https://www.pystone.net/notes/unity-enumerator-coroutine-internals/#%E4%BB%8Eenumerator%E5%88%B0startcoroutine%E5%BA%95%E5%B1%82%E5%88%86%E6%9E%90%E5%AE%B9%E5%99%A8%E7%9A%84%E6%A6%82%E5%BF%B5%E6%9E%9A%E4%B8%BE%E5%99%A8%E4%B8%8E%E4%BB%A3%E7%A0%81%E7%9A%84%E5%8D%8F%E7%A8%8B%E5%BC%82%E6%AD%A5%E6%89%A7%E8%A1%8C%E7%A9%B6%E7%AB%9F%E6%9C%89%E4%BB%80%E4%B9%88%E5%85%B3%E7%B3%BB"
    }
  ],
  "claims": [],
  "outgoing": [],
  "incoming": [
    {
      "id": "note:game-graphics-and-runtime",
      "title": "游戏图形与运行时",
      "url": "https://www.pystone.net/notes/game-graphics-and-runtime/",
      "atlas": "https://www.pystone.net/?node=game-graphics-and-runtime#knowledge-atlas",
      "label": "游戏图形与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "Unity中的“从Enumerator到StartCoroutine底层分析”导航项",
      "citation": "https://www.pystone.net/notes/game-graphics-and-runtime/#unity"
    }
  ],
  "contentMarkdown": "# 从Enumerator到StartCoroutine底层分析\n## 概述\n\n```\nyield\n\nreturn\n <expression>;\n\nyield\n\nbreak\n;\n```\n\n\n如果你在语句中使用 yield 上下文关键字，则意味着它在其中出现的方法、运算符或 get 访问器是迭代器，向编译器指示它所在的方法是迭代器块。\n\n\n使用 yield return 语句可一次返回一个元素。可通过使用 foreach 语句或 LINQ 查询来使用从迭代器方法返回的序列。 foreach 循环的每次迭代都会调用迭代器方法。 迭代器方法运行到 yield return 语句时，会返回一个 expression，并保留当前在代码中的位置。 下次调用迭代器函数时，将从该位置重新开始执行。\n\n\n在 yield return 语句中，将计算 expression 并将结果以值的形式返回给枚举器对象；expression 必须可以隐式转换为 yield 类型的迭代器。\n\n\n可以使用 yield break 语句来终止迭代。\n\n\n实现原理 - 编辑器遇到上面的情况会将生成一些类来保存yield return的所有调用都一一做分支处理（状态机分支），下次对IEnumerator.moveNext时会从上次的状态开始（如：迭代到第几个）\n\n\n## 迭代器模式的实现与Enumerator\n\n### foreach的原理\n\n在 C# 中，foreach 其实是一个语法糖，在编译成 IL 代码时，foreach 会变成基于迭代器 IEnumerator 的调用方式。\n\n\nforeach编译成的IL代码如下：\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615789031828.png)\n\n以下两段代码效果相同：\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615789060736.png)\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615789081096.png)\n\n\n### 迭代器模式的实现\n\n有许多种方法可以把对象堆起来成为一个集合，你可以把它们放进数组、链表、树、散列表等等不同的数据结构中，每一种对象的组织方式都有它的优点和适合的使用场景。\n\n迭代器模式使我们在遍历集合时，不需要清楚集合的内部实现方式。不同集合存储方式的细节不同，但遍历方式的统一的。\n\n\n> 设计思想原则：隐藏细节，开放接口\n\n\n遍历集合时，处理好三件事即可：\n\n\n- 能够以某种顺序一个一个访问集合中的元素\n- 能够获得当前元素的值\n- 能够重新回到集合的开头\n\n为此，IEnumerator接口分别定义了三个方法：MoveNext, get_Current, Reset\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615789423446.png)\n\n\n.NET 系统库提供的 List、ArrayList、Dictionary 等集合都实现了 Enumerator。以List为例看看具体的实现：\n\n`List<T>`实现了`IEnumerable<T>`和`IEnumerable`接口，这两个接口也就定义了`GetEnumerator`方法：\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615790017077.png)\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615790081591.png)\n\n\n而`Enumerator`是List类中的一个结构体类型，该结构体实现了`IEnumerator`接口。\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615790157682.png)\n\n\n当对`List<T>`调用GetEnumerator()方法时，返回的的`List<T>.Enumerator`类的一个对象。\n\n\n> 使用迭代器进行遍历时，不能进行Remove和Add操作，因为这两个操作会导致元素整体移动，导致当前Index指向的元素位置偏移，遍历结果错误。\n\n\n### 自己实现一个List\n\n```\nusing\n System.Collections;\n\nusing\n System;\n\n\npublic\n\nclass\n\nMyList\n<\nT\n> :\nIEnumerable\n\n\n{\n\n\nprivate\n\nstatic\n\nreadonly\n T[] _emptyArray =\nnew\n T[\n0\n];\n\n\nprivate\n\nconst\n\nint\n _defaultCapacity =\n4\n;\n\n\nprivate\n T[] _items;\n\n\nprivate\n\nint\n _size;\n\n\nprivate\n\nint\n _version;\n\n\nprivate\n Func<T,\nbool\n> _filterFunc;\n\n\npublic\n\nint\n Count => _size;\n\n IEnumerator IEnumerable.GetEnumerator()\n\n {\n\n\nreturn\n GetEnumerator();\n\n }\n\n\npublic\n\nMyList\n(\n)\n\n\n{\n\n\nthis\n._items = _emptyArray;\n\n }\n\n\n///\n\n<param name=\"func\">\n</param>\n\n\npublic\n\nMyList\n(\nFunc<T,\nbool\n> func\n)\n\n\n{\n\n\nthis\n._items = _emptyArray;\n\n _filterFunc = func;\n\n }\n\n\npublic\n IEnumerator\nGetEnumerator\n(\n)\n\n\n{\n\n\nreturn\n\nnew\n Enumerator(\nthis\n);\n\n }\n\n\npublic\n\nvoid\n\nAdd\n(\nT item\n)\n\n\n{\n\n\nif\n (\nthis\n._size ==\nthis\n._items.Length)\n\n\nthis\n.EnsureCapacity(\nthis\n._size +\n1\n);\n\n\nthis\n._items[\nthis\n._size++] = item;\n\n ++\nthis\n._version;\n\n }\n\n\n///\n\n<summary>\n\n\n///\n 扩容机制\n\n\n///\n\n</summary>\n\n\n///\n\n<param name=\"min\">\n</param>\n\n\nprivate\n\nvoid\n\nEnsureCapacity\n(\nint\n min\n)\n\n\n{\n\n\nif\n (\nthis\n._items.Length >= min)\n\n\nreturn\n;\n\n\nint\n num =\nthis\n._items.Length ==\n0\n ?\n4\n :\nthis\n._items.Length *\n2\n;\n\n\nif\n ((\nuint\n)num >\n2146435071\nU)\n\n num =\n2146435071\n;\n\n\nif\n (num < min)\n\n num = min;\n\n\nthis\n.Capacity = num;\n\n }\n\n\npublic\n\nint\n Capacity\n\n {\n\n\nget\n =>\nthis\n._items.Length;\n\n\nset\n\n//有点子监听的感觉\n\n\n {\n\n\nif\n (\nvalue\n ==\nthis\n._items.Length)\n\n\nreturn\n;\n\n\nif\n (\nvalue\n >\n0\n)\n\n {\n\n T[] objArray =\nnew\n T[\nvalue\n];\n\n\nif\n (\nthis\n._size >\n0\n)\n//扩容真正实现在这里\n\n\n Array.Copy((Array)\nthis\n._items,\n0\n, (Array)objArray,\n0\n,\nthis\n._size);\n\n\nthis\n._items = objArray;\n\n }\n\n\nelse\n\n\nthis\n._items = _emptyArray;\n\n }\n\n }\n\n\n///\n\n<summary>\n\n\n///\n 迭代器定义\n\n\n///\n\n</summary>\n\n\npublic\n\nstruct\n Enumerator : IEnumerator\n\n {\n\n\nprivate\n MyList<T> _list;\n\n\nprivate\n\nint\n _index;\n\n\nprivate\n\nint\n _version;\n\n\nprivate\n T current;\n\n\ninternal\n\nEnumerator\n(\nMyList<T> list\n)\n\n\n{\n\n\nthis\n._list = list;\n\n\nthis\n._index =\n0\n;\n\n\nthis\n._version = list._version;\n\n\nthis\n.current =\ndefault\n(T);\n\n }\n\n\npublic\n\nbool\n\nMoveNext\n(\n)\n\n\n{\n\n\nif\n (\nthis\n._version != _list._version || (\nuint\n)\nthis\n._index >= (\nuint\n)_list._size)\n\n\nreturn\n\nthis\n.MoveNextRare();\n\n\nthis\n.current = _list._items[\nthis\n._index];\n\n ++\nthis\n._index;\n\n\n//当不满足过滤条件时，foreach遍历Current返回为空\n\n\nif\n (_list._filterFunc !=\nnull\n && !_list._filterFunc(\nthis\n.current))\n\n\nthis\n.current =\ndefault\n(T);\n\n\nreturn\n\ntrue\n;\n\n }\n\n\nprivate\n\nbool\n\nMoveNextRare\n(\n)\n\n\n{\n\n\nthis\n._index =\nthis\n._list._size +\n1\n;\n\n\nthis\n.current =\ndefault\n(T);\n\n\nreturn\n\nfalse\n;\n\n }\n\n\npublic\n\nobject\n Current =>\nthis\n.current;\n\n\npublic\n\nvoid\n\nReset\n(\n)\n\n\n{\n\n _index =\n0\n;\n\n current =\ndefault\n(T);\n\n }\n\n }\n\n}\n```\n\n\n## Enumerator与yield return\n\nYou use a yield return statement to **return each element one at a time**.\n\n\nWhen a yield return statement is reached in the iterator method, **expression is returned**, and **the current location in code is retained**. Execution is restarted **from that location the next time** that the iterator function is called.\n\n\n```\npublic\n\nstatic\n\nvoid\n\nTestGetVs\n(\n)\n\n\n{\n\n IEnumerator<\nint\n> enumerator = GetVs();\n\n\nwhile\n(enumerator.MoveNext())\n\n {\n\n\nint\n current = enumerator.Current;\n\n Debug.Log(current);\n\n }\n\n}\n\n\npublic\n\nstatic\n IEnumerator<\nint\n>\nGetVs\n(\n)\n\n\n{\n\n\n//for (int i = 0; i < 3; i++)\n\n\n//{\n\n\n// yield return i;\n\n\n//}\n\n\nyield\n\nreturn\n\n3\n;\n\n\nyield\n\nreturn\n\n4\n;\n\n\nyield\n\nreturn\n\n5\n;\n\n}\n```\n\n\n底层原理：\n\n如果写了一个函数，返回值类型为IEnumerator，方法体中有yield return，那么编译器会为这个方法自动定义一个类型，这个类型实现IEnumerator接口。\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615794880255.png)\n\n\n这个类型有两个字段：\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615794868354.png)\n\nstate保存当前执行到的位置，current为当前的返回值。\n\n\n使用foreach对这个方法的返回值进行遍历，实质上就是对这个迭代器进行遍历。每次调用MoveNext()，从state指向的位置开始执行，直到遇到下一个yield return，将返回的结果存到current当中，保存当前位置到state中。MoveNext函数的IL语言形式如下：\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615795022810.png)\n\n\n总结：使用IEnumerator与yield return的方式，可以实现有多个返回值的方法，对多个返回值进行遍历，每访问一个值，执行一段代码，不需要先计算出所有的值，再进行遍历。其本质是把函数(例子当中为GetVs)的代码放到Enumerator类型的MoveNext当中，用state进行控制，每调用一次，执行不同的段落。\n\n\n## Unity3D协程\n\nRef: <https://www.cnblogs.com/Roz-001/p/11205700.html>\n\n\n![](assets/0052 - 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？__1615796287192.png)\n\n以上代码，实质上是调用Test2Sceonds方法，返回一个Enumerator对象，然后传递给StartCoroutine方法。\n\n\nStartCoroutine是在Native层实现的。从行为上来看，当StartCoroutine后，Unity会每一帧执行一次MoveNext。\n\n\n如果yield return 返回的对象为WaitForSecond等，Unity会据此判断下一次调用MoveNext是什么时候。如以上代码，Unity会在2秒后，再次调用MoveNext。\n\n\n### 本质是什么\n\n协程是一种异步执行代码的方式，是一种假的多线程。本质上，是**将一件任务，分到多个帧中去执行**。\n\n\n多线程（并发）与迭代器、枚举器、容器是毫不相干的两个概念，Unity为什么就把这两个概念联系到一起了呢？为什么实现协程要用到枚举类型？这种实现方案，本质上是用了Enumerator的什么特性呢？这是笔者一直困惑的。\n\n\n如果一个技术A，要用到技术B，那么A的需求，一定与B的特性是有共同点的，否则A不会用到B。那么我们从这个角度去思考上述问题。\n\n\n- 协程，本质是异步执行，把一个任务（一段代码），分多帧去执行。\n- .NET当中的Enumerator与yield return的机制，恰好本质上是把一段代码拆分为多个段落（部分），每次调用MoveNext，执行一段逻辑，获取一个返回值。\n\n那么，两个技术自然就关联到了一起——Unity对Enumerator的MoveNext进行调用，就实现了将一个任务分多次去执行的需求。每一帧调用一次MoveNext，就实现了每一帧执行一部分任务。而Native层还对yield return返回的类型进行了判断，根据返回类型的不同 ，控制下一次调用MoveNext的时机，例如，如果返回值是new WaitForSeconds(2)，那么Unity会在2秒后调用下一个MoveNext，并获取返回值。如果返回值是UnityWebRequest.SendWebRequest，那么Unity会在相应的请求执行完后，继续执行后面的部分。\n\n\n### yield return 的一些特殊含义\n\n```\n//程序在下一帧中从当前位置继续执行\n\n\nyield\n\nreturn\n\n0\n;\n\n\n//程序在下一帧中从当前位置继续执行\n\n\nyield\n\nreturn\n\nnull\n;\n\n\n//程序等待N秒后从当前位置继续执行\n\n\nyield\n return new\nWaitForSeconds\n(\nN\n)\n;\n\n\n//在所有的渲染以及GUI程序执行完成后从当前位置继续执行\n\n\nyield\n new\nWaitForEndOfFrame\n(\n)\n;\n\n\n//所有脚本中的FixedUpdate()函数都被执行后从当前位置继续执行\n\n\nyield\n new\nWaitForFixedUpdate\n(\n)\n;\n\n\n//等待一个网络请求完成后从当前位置继续执行\n\n\nyield\n\nreturn\n WWW;\n\n\n//等待一个xxx的协程执行完成后从当前位置继续执行\n\n\nyield\n return\nStartCoroutine\n(\nxxx\n)\n;\n\n\n//如果使用yield break语句，将会导致协程的执行条件不被满足，不会从当前的位置继续执行程序，而是直接从当前位置跳出函数体，回到函数的根部\n\n\nyield\n\nbreak\n;\n\n\nyield\n\nreturn\n IEnumerator自己定义实现类，等待到自定义的时间或事件结束。\n```\n\n\n### 一些Demo\n\n```\n//下面定义了一个协程函数，注意必须使用IEnumerator作为返还值才能成为协程函数。\n\n\nIEnumerator\nTest\n(\n)\n\n\n{\n\n\nfor\n(\nint\n i =\n0\n; i<\n1000\n ; ++i)\n\n {\n\n ans += i;\nyield\n\nreturn\n\n0\n;\n//挂起，下一帧再来从这个位置继续执行。\n\n\n }\n\n j+=\n2\n;\n\n\nyield\n\nreturn\n\n0\n;\n//挂起，下一帧再来从这个位置继续执行。 ++j;\n\n\nyield\n\nreturn\n\n0\n;\n//挂起，下一帧再来从这个位置继续执行。\n\n\n}\n```\n\n\nDemo1:\n\n\n```\n//3s后执行Debug.Log\n\n\nIEnumerator\nTest\n(\n)\n\n\n{\n\n\nfor\n(\nfloat\n timer =\n0.0\nf; timer <\n3.0\nf ; timer += Time.DeltaTime){\n\n\nyield\n\nreturn\n\n0\n;\n//挂起，下一帧再来从这个位置继续执行。\n\n\n }\n\n Debug.Log(\n\"启动协程3s后\"\n);\n\n}\n```\n\n\nDemo2:\n\n\n```\nIEnumerator\nTestWaitForSeconds\n(\n)\n\n\n{\n\n\n//3s后执行Debug.Log;\n\n\nyield\n return new\nWaitForSeconds\n(\n3.0\nf\n)\n;\n\n Debug.Log(\n\"启动协程3s后\"\n);\n\n}\n```\n\n\n### AssetBundle异步加载\n\n```\n\n///\n\n<summary>\n\n\n///\n 从文件进行异步加载\n\n\n///\n\n</summary>\n\n\n///\n\n<param name=\"abFullPath\">\nab包的绝对路径\n</param>\n\n\n///\n\n<param name=\"finishCallBack\">\n完成加载回调，返回加载的AssetBundle\n</param>\n\n\n///\n\n<param name=\"updateCallBack\">\n加载过程中的更新回调，返回加载进度\n</param>\n\n\n///\n\n<returns>\n</returns>\n\n\nIEnumerator\nLoadABFileAsync\n(\nstring\n abFullPath,Action<AssetBundle> finishCallBack, Action<\nfloat\n> updateCallBack=\nnull\n)\n\n\n{\n\n AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(abFullPath);\n\n\nwhile\n (!request.isDone)\n\n {\n\n\nif\n(updateCallBack !=\nnull\n)\n\n {\n\n updateCallBack(request.progress);\n\n }\n\n\nyield\n\nreturn\n\nnull\n;\n\n\n }\n\n\nyield\n\nreturn\n request;\n\n\nif\n(finishCallBack!=\nnull\n)\n\n finishCallBack(request.assetBundle);\n\n }\n\n\nprivate\n\nvoid\n\nStart\n(\n)\n\n\n{\n\n assetPath = Application.dataPath +\n\"/../MyAssetBundles/\"\n;\n\n StartCoroutine(LoadABFileAsync(assetPath + assetBundleName, OnFinish, OnUpdate));\n\n\n }\n\n\nprivate\n\nvoid\n\nOnUpdate\n(\nfloat\n obj\n)\n\n\n{\n\n Debug.Log(obj);\n\n }\n\n\nprivate\n\nvoid\n\nOnFinish\n(\nAssetBundle obj\n)\n\n\n{\n\n Instantiate(obj.LoadAsset(loadPrefabName));\n\n obj.Unload(\nfalse\n);\n\n }\n```\n\n\n### UnityWebRequest\n\n```\nusing\n UnityEngine;\n\nusing\n UnityEngine.Networking;\n\nusing\n System.Collections;\n\n\npublic\n\nclass\n\nTestWebRequest\n :\nMonoBehaviour\n {\n\n\nvoid\n\nStart\n(\n)\n{\n\n StartCoroutine(GetText());\n\n }\n\n\nIEnumerator\nGetText\n(\n)\n{\n\n UnityWebRequest www =\nnew\n UnityWebRequest(\n\"http://www.my-server.com\"\n);\n\n www.downloadHandler =\nnew\n DownloadHandlerBuffer();\n\n\nyield\n\nreturn\n www.SendWebRequest();\n\n\nif\n (www.result != UnityWebRequest.Result.Success) {\n\n Debug.Log(www.error);\n\n }\n\n\nelse\n {\n\n\n// Show results as text\n\n\n Debug.Log(www.downloadHandler.text);\n\n\n// Or retrieve results as binary data\n\n\nbyte\n[] results = www.downloadHandler.data;\n\n }\n\n }\n\n}\n```\n\n\n## Ref:\n\n<https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/yield>\n\n## 从Enumerator到StartCoroutine底层分析——容器的概念枚举器与代码的协程、异步执行究竟有什么关系？\n@(4. Unity3D)\n\n[TOC]\n"
}
