6

我读过关于 和 之间区别的Directory.EnumerateFiles讨论Directory.GetFiles

我知道他们在内部都使用 System.IO.FileSystemEnumerableFactory.CreateFileNameIterator()

不同之处在于EnumerateFiles可能使用延迟执行(惰性),而GetFiles()使用 a ToArray,因此该函数已经执行。

但是如果在迭代过程中将文件和文件夹添加到字典中会发生什么。迭代是否只迭代在EnumerateFiles()?

更糟糕的是:如果在迭代期间删除文件会发生什么:它们还会被迭代吗?

4

3 回答 3

6

谢谢米哈尔科莫罗夫斯基。然而,当我自己尝试他的解决方案时,我看到了 Directory.EnumerateFiles 和 Directory.GetFiles() 之间的显着区别:

Directory.CreateDirectory(@"c:\MyTest");
// Create fies: b c e
File.CreateText(@"c:\MyTest\b.txt").Dispose();
File.CreateText(@"c:\MyTest\c.txt").Dispose();
File.CreateText(@"c:\MyTest\e.txt").Dispose();

string[] files = Directory.GetFiles(@"c:\MyTest");
var fileEnumerator = Directory.EnumerateFiles(@"c:\MyTest");

// delete file c; create file a d f
File.Delete(@"c:\MyTest\c.txt");
File.CreateText(@"c:\MyTest\a.txt").Dispose();
File.CreateText(@"c:\MyTest\d.txt").Dispose();
File.CreateText(@"c:\MyTest\f.txt").Dispose();

Console.WriteLine("Result from Directory.GetFiles");
foreach (var file in files) Console.WriteLine(file);
Console.WriteLine("Result from Directory.EnumerateFiles");
foreach (var file in fileEnumerator) Console.WriteLine(file);

这将给出不同的输出。

Result from Directory.GetFiles
c:\MyTest\b.txt
c:\MyTest\c.txt
c:\MyTest\e.txt
Result from Directory.EnumerateFiles
c:\MyTest\b.txt
c:\MyTest\d.txt
c:\MyTest\e.txt
c:\MyTest\f.txt

结果:

  • GetFiles 仍然看到旧文件:BCE 符合预期
  • EnumerateFiles 看到了新文件 D 和 F。它正确地跳过了已删除的文件 C,但它错过了新文件 A。

所以 EnumerateFiles 和 GetFiles 在使用上的区别不仅仅是性能。

  • GetFiles 返回您调用该函数时文件夹中的文件。这是可以预料的,因为它只是对字符串集合的枚举
  • EnumerateFiles 正确跳过删除的文件,但看不到所有添加的文件。如果在枚举结果时文件夹发生更改,则结果是相当不确定的。

因此,如果您希望您的文件夹在仔细枚举时发生变化,请选择所需的功能

  • 期望 GetFiles 查看已删除的文件
  • 预计 EnumerateFiles 会错过一些新文件。
于 2015-04-10T09:35:08.957 回答
0

只有一种方法可以检查:

Directory.CreateDirectory(@"c:\\Temp");
File.Create(@"c:\\Temp\\a.txt").Close();
File.Create(@"c:\\Temp\\b.txt").Close();
File.Create(@"c:\\Temp\\c.txt").Close();
foreach (var f in Directory.EnumerateFiles(@"c:\\Temp"))
{
    Console.WriteLine(f);
    //Let's delete a file
    File.Delete(@"c:\\Temp\\c.txt");
    //Let's create a new file
    File.Create(@"c:\\Temp\\d.txt").Close();
}

最初C:\Temp包含 3 个文件:a.txt、b.txt 和 c.txt。在迭代期间,这些文件中的一个正在被删除,一个正在被创建。最后,C:\Temp包含以下文件:a.txt、b.txt 和 d.txt 但是,在控制台中您将看到该目录的原始内容,即:

c:\\Temp\a.txt
c:\\Temp\b.txt
c:\\Temp\c.txt
于 2015-04-10T07:56:11.023 回答
0

我做了一个不同的实验,因为我对文件枚举缓慢的情况感兴趣,而在枚举目录中创建了更多文件。例如,如果SemaphoreSlim.WaitAsync枚举循环内部有一个(出于节流目的),则可能会发生缓慢枚举的情况。下面的实验首先从目标目录中删除所有文件,然后创建特定数量的初始文件,然后以 100 毫秒的延迟开始枚举文件,而另一个异步工作流以每 150 毫秒一个文件的速率创建更多文件. 枚举器会看到新创建的文件吗?

static async Task Main(string[] args)
{
    const string FOLDER_PATH = @"C:\DirectoryEnumerateFilesTest";
    const int FILES_COUNT = 10;
    Console.WriteLine($"Deleting files");
    DeleteAllFiles(FOLDER_PATH);
    Console.WriteLine($"Creating files");
    await CreateFiles(FOLDER_PATH, startIndex: 1, filesCount: FILES_COUNT, delay: 0);
    Console.WriteLine($"Enumerating files while creating more files");
    var filePaths = Directory.EnumerateFiles(FOLDER_PATH);
    var cts = new CancellationTokenSource();
    var producer = CreateFiles(FOLDER_PATH,
        startIndex: 501, filesCount: 100, delay: 150, cts.Token);
    var enumeratedCount = 0;
    foreach (var filePath in filePaths)
    {
        Console.WriteLine($"Enumerated:   {Path.GetFileName(filePath)}");
        await Task.Delay(100);
        enumeratedCount++;
    }
    Console.WriteLine($"Total files enumerated: {enumeratedCount:#,0}");
    cts.Cancel();
    await producer;
}

private static void DeleteAllFiles(string folderPath)
{
    int count = 0;
    foreach (var filePath in Directory.GetFiles(folderPath))
    {
        File.Delete(filePath);
        Console.WriteLine($"File deleted: {Path.GetFileName(filePath)}");
        count++;
    }
    Console.WriteLine($"Total files deleted: {count:#,0}");
}

private static async Task CreateFiles(string folderPath,
    int startIndex, int filesCount, int delay, CancellationToken token = default)
{
    int count = 0;
    foreach (var i in Enumerable.Range(startIndex, filesCount))
    {
        var delayTask = Task.Delay(delay, token);
        await Task.WhenAny(delayTask);
        if (delayTask.IsCanceled) break;
        var fileName = $"File-{i:000}.txt";
        var filePath = Path.Combine(folderPath, fileName);
        File.WriteAllText(filePath, "Content");
        count++;
        Console.WriteLine($"File created: {fileName}");
    }
    Console.WriteLine($"Total files created: {count:#,0}");
}

答案是:这取决于初始文件的数量和文件名的长度。阈值约为 50 个初始文件,但当文件的文件名较长时,阈值会变小。枚举最终将停止,前提是枚举器的工作速度比文件生产者快,在这种情况下,许多文件将保持未观察到(通常大约 20 个)。

这是上述实验的输出FILES_COUNT = 10(意味着在创建枚举器时有 10 个现有文件)。

Deleting files
Total files deleted: 0
Creating files
File created: File-001.txt
File created: File-002.txt
File created: File-003.txt
File created: File-004.txt
File created: File-005.txt
File created: File-006.txt
File created: File-007.txt
File created: File-008.txt
File created: File-009.txt
File created: File-010.txt
Total files created: 10
Enumerating files while creating more files
Enumerated:   File-001.txt
Enumerated:   File-002.txt
File created: File-501.txt
Enumerated:   File-003.txt
File created: File-502.txt
Enumerated:   File-004.txt
Enumerated:   File-005.txt
File created: File-503.txt
Enumerated:   File-006.txt
File created: File-504.txt
Enumerated:   File-007.txt
Enumerated:   File-008.txt
File created: File-505.txt
Enumerated:   File-009.txt
File created: File-506.txt
Enumerated:   File-010.txt
Total files enumerated: 10
File created: File-507.txt
Total files created: 7

10 个文件太少,所以后来创建的文件都没有被枚举员观察到。

于 2020-03-24T10:25:23.943 回答