在没有ToList()的情况下运行时,在Linq的SelectMany()中读取文件会抛出System.IO.IOException

问题描述 投票:2回答:2

我很好奇为什么下面的代码失败了System.IO.IOException,说该进程无法访问该文件,因为它被另一个进程使用。

class Program
{
    static void Main(string[] args)
    {
        const string filename = "tmp.txt";
        File.AppendAllText(filename, "x");
        var output = Enumerable.Range(0, 10).SelectMany(_ => File.ReadAllLines(filename));
        File.WriteAllLines(filename, output);
    }
}

通过简单地在Linq管道的末端抛出一个ToList(),我避免了这个问题,但我无法弄清楚为什么会这样。

c# linq io
2个回答
4
投票

你看到了Linq懒惰和物化的影响。

随着.ToList()的添加,我们实现了output:Linq执行查询(打开文件,填充集合并关闭文件):

 // List<T> collection
 var output = Enumerable
   .Range(0, 10)
   .SelectMany(_ => File.ReadAllLines(filename))
   .ToList();  // <- We want a collection in memory

所以当它是时候写,我们从内存中写入数据做磁盘

 // Writing down the collection
 File.WriteAllLines(filename, output);

没有.ToList() Linq(懒惰)什么都不做:

 // IEnumerable<T> 
 var output = Enumerable
   .Range(0, 10)
   .SelectMany(_ => File.ReadAllLines(filename)); // <- No more than a declaration

什么时候写

 File.WriteAllLines(filename, output);

Linq发现它必须提供应该写入的数据 - output并开始执行此操作:它尝试打开文件但在此操作中失败,因为该文件已使用File.WriteAllLines打开。


0
投票

因为您尝试同时读取和写入同一文件,

ToList()导致执行(枚举)发生在那一点,SelectMany返回一个IEnumerable并等待它在File.WriteAllLines(filename, output)执行(枚举)

© www.soinside.com 2019 - 2024. All rights reserved.