【问题标题】:Does File.ReadLines(filePath).First() close the file immediately? [duplicate]File.ReadLines(filePath).First() 是否立即关闭文件? [复制]
【发布时间】:2021-09-15 08:46:13
【问题描述】:

我知道在foreach 循环中使用File.ReadLines() 返回的IEnumerable 时,文件会在循环后自动关闭。 我只需要快速检查文件的第一行。这足够吗?还是会让文件保持打开状态?

protected void Append(string filePath, Encoding encoding)
{
    try
    {
        string firstLine = File.ReadLines(filePath, encoding).First();
        // more code here
    }
    catch
    {
        // more code here
    }
}

【问题讨论】:

标签: c# .net file enumerable


【解决方案1】:

(请注意,这是用于返回 IEnumerable<String>File.ReadLines() - 这不适用于返回 String[]File.ReadAllLines()。)


File.ReadLines(filePath).First() 会立即关闭文件吗?

是的

...假设“立即”是指当 整个语句 完成时 - 不仅仅是内部的 ReadLines() 子表达式。


  • 在内部,File.ReadLines() 返回一个 instance of ReadLinesIterator - 这是一个 IEnumerable<T>
  • IEnumerable<T> 被迭代时,C#/.NET 使用IEnumerable<T>.GetEnumerator<T>(),它返回一个IEnumerator<T>必须在程序完成迭代后处理它想要的内容。
    • 因为IEnumerator<T> 实例必须 被处理,所以我们始终鼓励您使用foreach 为您处理此问题(而不是自己手动处理IEnumerator<T>)。
      • 如果在 foreach 循环体内引发异常,foreach 还将确保释放 IEnumerator<T>
  • 在这种特定情况下,ReadLinesIterator 包含一个StreamReader(其中包含打开的FileStream)。当ReadLinesIterator 被处理时,内部StreamReader 被关闭,这反过来又关闭FileStream
  • .Frist() 方法是 Linq 的 Enumerable.First( IEnumerable<T> source )
    • 在内部,Linq 的 First() 与调用 foreach( T item in source ) 并立即在 foreach 中返回相同 - 所以 First 方法将为您处理 ReadLinesIterator

安全

我注意到 ReadLinesIterator is both an IEnumerator<T> and an IEnumerable<T> 并包装了一个打开的 StreamReader - 这确实意味着您在使用 ReadLines() 时需要小心,以确保您看到的 IEnumerable<T> 实际上是迭代的, 否则你有泄漏打开文件句柄的风险。

...这也意味着,如果您使用的是 Linq 方法链,并且在 Linq 链内部但在任何 Linq 内部 try/catch/foreach/@987654361 之外发生异常@ 块然后您将泄漏一个文件句柄,该文件句柄在 GC 最终确定 ReadLinesIterator 之前不会关闭...尽管我承认我很难想到什么时候会发生这种情况。

【讨论】:

  • 问题已作为重复项关闭。我认为所谓的重复文件并没有详细解释您所做的事情。我对 Linq 方法 First() 将如何影响文件特别感兴趣,这绝对不能从声称的副本中推断出来。谢谢你的回答。
【解决方案2】:

File.ReadLines 返回IEnumerable<string>。 Enumerable 使用IEnumerator<string> 读取行,是一次性的。

读取第一行并保证资源安全的正确方法:

var lines = File.ReadLines(path);
string firstLine;
using (var it = lines.GetEnumerator())
{
    if (!it.MoveNext())
        throw new InvalidOperationException("File is empty");
    firstLine = it.Current;
}

UseLine(firstLine);

这很复杂,但肯定是安全的

【讨论】:

  • 您的代码与ReadLines().First() 所做的完全相同
  • 只需阅读您的评论并开源,它就会处理。我没想到会这样。你是对的
【解决方案3】:

使用流阅读器读取文件的最佳实践 id。

var lines = new List<string>();

using (StreamReader reader = new StreamReader(@"C:\test.txt")) {
    var line = reader.ReadLine();

    while (line != null) {
        lines.Add(line);
        line = reader.ReadLine();
    }
}

一旦代码移出 using 语句,填充对象将被释放,wfile 将被关闭。 File.ReadLines 从不关闭文件。

【讨论】:

  • File.ReadLines - 它在返回的枚举数被释放时执行 - foreach 为您执行此操作。
  • 我不同意你的方法是“最佳实践”——你在一个扩展列表中缓冲内存中的每一行——这与 File.ReadAllLines 做同样的事情。如果您只想单独处理行,这种方法会浪费内存。
  • @Dai 方法是读取和关闭文件。
  • @Amit 但我只想要最快最安全的方法来只读取第一行,因为文件很大。
猜你喜欢
  • 2011-09-15
  • 2013-04-30
  • 2020-12-05
  • 2015-07-16
  • 2015-10-20
  • 1970-01-01
  • 2011-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多