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