在我的最新帖子中,我使用了名为“ EnumerateFiles” 的DirectoryInfo方法。但是,如果我们研究DirectoryInfo的可用的方法,我们将注意到还有一个“ GetFiles”方法,它具有相同的重载和参数个数,但就是这两种方法之间的差异,以及为什么我选择使用“ EnumerateFiles”方法。
那么,回答这个问题,我们将必须查看.NET的内部工作中发生了什么,为此,我们还必须查看Enumerators 是什么以及它们是如何工作的。
让我们从foreach构造开始。
因为foreach听起来很像for构造,所以有些人可能认为foreach是一种更花哨的方式来使用for外观,而不使用其他变量来保持对我们所在位置的计数。
我们知道,for循环定义如下:
for(int index; index < Number; index++) { ...code to execute... }
而foreach循环定义是这样的:
foreach(var item in COLLECTION) { ... code to execute ... }
但.NET框架如何在没有条件存在的情况下知道何时停止?嗯,简单的答案是,它不知道何时停止。我的意思是.NET Framework使用.NET中的Iterators概念或更常见的作为Enumerators。
基本上,在.NET中找到的所有集合都与Enumerators 一起使用,这对于foreach循环是必需的。
所以,让我们更深入一些,Enumerator是一个对象,它具有一个Current属性和两个名为Reset和MoveNext的方法,所以如果我们有一个集合,比如说一个项目的列表,我们在foreach循环中使用它,那么foreach将调用MoveNext,它将Current属性设置为列表中的下一个项目,如果下一个对象被发现,则返回true或false,但如果在该列表中找不到更多对象,则该Current对象将设置为null。
使用这个工作流程,我们还可以使用.NET中的另一个循环,并手动调用MoveNext方法和Current属性,实际上有一些算法仍在使用手动调用。
因此,如果我们要实现一个Enumerator,它只给我们偶数的数字,我们将实现这样的MoveNext方法:
public bool MoveNext() { Current = Current + 2; return true; }
现在,如果你注意到,前面我们只返回true,这意味着,如果这个Enumerator是与foreach循环被一起使用,那么它将永远运行,除非我们阻止它,或者如果我们设置了overflow检查,直到它达到int的最大大小和然后抛出异常。
使用这个工作流程的另一个结果(你们中的一些人可能会遇到它)是,如果我们将foreach循环与集合一起使用,那么我们就无法修改该循环内的集合,如果我们尝试修改,那么将抛出异常。
但是Enumerator除了允许我们使用foreach构造之外,还有另一个好处,那就是我们在执行MoveNext方法时一次使用一个对象。
回到我们的“ GetFiles” vs “EnumerateFiles” 的例子,让我们用它作为一个包含1000个文件的文件夹的例子。
当我们调用“ GetFiles”方法时,我们将收到该目录中所有1000个文件的数组,但是我们还必须等到方法遍历每个文件,将其转换为 FileInfo并将其添加到其内部数组,然后再返回给我们。之后,我们可以循环浏览这些文件,并利用它们进行工作。
另一方面,当我们调用“ EnumerateFiles”方法时,该方法将查找文件,获取它遇到的第一个文件,将其返回给我们,我们执行我们想要针对该文件的工作,然后我们将进入下一个文件。
现在想象那个目录或文件夹,其中有成千上万的文件,其中一些甚至嵌套在其他文件夹中的更深处,然后使用“ GetFiles”方法是非常低效的,更糟糕的是,每次我们调用方法时,我们得到一个整个集合,这意味着如果我们想要并行处理多个文件,我们必须管理哪个循环与哪个文件一起工作,这样我们就不会重叠,在这种情况下foreach循环利用这种Enumerator,将使我们的工作更容易,因为我们一次只能处理一件文件,每次我们申请新文件时,我们都会继续前进。
原文地址:https://www.codeproject.com/Articles/1266989/IEnumerator-and-foreach