【FastReport教程】在C#中的Foreach
【下载FastReport.Net最新版本】
我们都知道foreach循环是 - 循环遍历集合的所有元素。它在易用性方面的最大优势 - 我们不需要担心集合中有多少元素。然而,许多人不知道这只是语法“sugar”,这有利于程序员的工作。因此,我们只需知道生成的编译器将转换的内容。foreach循环的工作方式不同,具体取决于您要排序的集合。
(1)如果必须处理平凡阵列,我们总能知道它的长度。因此,foreach最终将转换为for循环。例如:
int[] array = new int[]{1, 2, 3, 4, 5, 6}; foreach (int item in array) { Console.WriteLine(item); }
编译器将循环转换为此构造:
int[] temp; int[] array = new int[]{1, 2, 3, 4, 5, 6}; temp = array; for (int i = 0; i < temp.Length; i++) { int item = temp[i]; Console.WriteLine(item); }
(2)但是,许多集合不支持对元素的索引访问,例如:Dictionary,Queue,Stack。在这种情况下,将使用迭代器模板。此模板基于接口System.Collections.Generic.IEnumerator 和非通用System.Collections.IEnumerator,它允许您迭代集合中的元素。IEnumerator包含:
- MoveNext()方法 - 将枚举数移动到集合的下一个元素;
- Reset()方法 - 重新启动枚举,将枚举数设置为起始位置;
- Current属性 - 返回集合的当前元素。
IEnumirator 继承自两个接口 - IEnumirator和IDisposable。它包含Current属性的重载,按类型提供其实现。既然我们提到了接口IDisposable,那么我们就会说几句话。它包含释放资源所需的唯一Dispose()方法。每次循环终止或退出时,IEnumirator 都会清除资源。我们来看看这个循环:
System.Collections.Generic.Queue<int> queue = new System.Collections.Generic.Queue<int>(); queue.Enqueue(1); queue.Enqueue(2); queue.Enqueue(3); foreach (int item in queue) { Console.WriteLine(item); }
编译器将其转换为类似的代码:
System.Collections.Generic.Queue<int> queue = new System.Collections.Generic.Queue<int>(); queue.Enqueue(1); queue.Enqueue(2); queue.Enqueue(3); int num; while (queue.MoveNext()) { num = queue.Current; Console.WriteLine(num); }
在此示例中,MoveNext替换了在循环期间计算元素的需要。当它没有收到下一个元素时,它返回fasle并且循环终止。 但是,尽管如此,这段代码只是近似于编译器的真正产生。问题是,如果您有两个或更多重叠循环使用相同的集合,则每个MoveNext调用将影响所有循环。这个过程不适合任何人。想出了第二个接口IEnumirator。
它包含唯一的方法GetEnumerator(),它返回一个枚举器。因此,IEnumerable 及其IEnumerable的通用版本允许您呈现枚举集合类中的元素的逻辑。通常,这是一个嵌套类,可以访问集合的元素并支持IEnumerator 。拥有每个枚举器,不同的消费者不会相互干扰,同时执行集合的枚举。 因此,上面的例子应该考虑两点 - 获得一个枚举器和释放资源。以下是编译器实际转换foreach循环代码的方法:
System.Collections.Generic.Queue<int> queue = new System.Collections.Generic.Queue<int>(); System.Collections.Generic.Queue<int>.Enumerator enumirator; IDisposable disposable; enumirator = queue.GetEnumerator(); queue.Enqueue(1); queue.Enqueue(2); queue.Enqueue(3); try { int num; while (enumirator.MoveNext()) { num = enumirator.Current; Console.WriteLine(num); } } finally { disposable = (IDisposable)enumirator; disposable.Dispose(); }
您可能认为对于集合的迭代,您需要实现IEnumerable和IEnumerable 接口。但是,这并不完全正确。要编译foreach,只需要实现GetEnumerator()方法,该方法将返回另一个具有Current属性和MoveNext()方法的对象。 这里我们使用duck typing - 一种众所周知的方法。
也就是说,如果有一个GetEnumerator()方法的对象,它返回一个带有MoveNext()方法和Current属性的对象,那么这就是枚举器。 否则,如果找不到必要的对象,并且找不到必要的方法,则将搜索IEnumerable和IEnumerable 接口。 因此,foreach实际上是一个通用循环,可以同时适用于数组和集合。我经常使用它。但是,foreach有一个缺点 - 它只允许读取元素,并且不允许更改它们。