File_adLines上的IEnumerable.Take(0)似乎没有处理/关闭File句柄

Tit*_*tus 27 c#

我有一个函数,它使用with 和combination 来跳过n代码y行并从给定文件中获取行.当我尝试打开下次给出的文件时:File.ReadLinesSkipTakefilePath

string[] Lines = File.ReadLines(filePath).Skip(0).Take(0).ToArray();
using (StreamWriter streamWriter = new StreamWriter(filePath))
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

File in use by another process在" using"行上有一个例外.

它看起来像是IEnumerable.Take(0)罪魁祸首,因为它返回一个空IEnumerable而不枚举返回的对象File.ReadLines(),我相信它不会处理文件.

我对吗?他们不应该列举以避免这种错误吗?怎么做得好?

Jon*_*eet 39

这基本上是一个错误File.ReadLines,而不是Take.ReadLines返回一个IEnumerable<T>,在逻辑上应该是懒惰的,但它急切地打开文件.除非您实际迭代返回值,否则您无需处置任何内容.

它仅在迭代一次方面被打破了.例如,您应该能够写:

var lines = File.ReadLines("text.txt");
var query = from line1 in lines
            from line2 in lines
            select line1 + line2;
Run Code Online (Sandbox Code Playgroud)

...应该给出文件中的行的交叉产品.由于破碎,它没有.

File.ReadLines 应该实现这样的事情:

public static IEnumerable<string> ReadLines(string filename)
{
    return ReadLines(() => File.OpenText(filename));
}

private static IEnumerable<string> ReadLines(Func<TextReader> readerProvider)
{
    using (var reader = readerProvider())
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,它不是:(

选项:

  • 使用上面的代替 File.ReadLines
  • 编写自己的实现Take,总是开始迭代,例如

    public static IEnumerable<T> Take<T>(this IEnumerable<T> source, int count)
    {
        // TODO: Argument validation
        using (var iterator = source.GetEnumerator())
        {
            while (count > 0 && iterator.MoveNext())
            {
                count--;
                yield return iterator.Current;
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 让Jon Skeet出现并告诉你BCL实现是疯狂的而你的代码不是. (7认同)
  • "`File.ReadLines` _should_可以像这样实现"像老板一样. (6认同)

Cod*_*ter 18

File.ReadLines()参考资料中的上述评论可以看出,负责团队知道这个"错误":

无法更改以保持与4.0兼容的已知问题:

  • 底层StreamReader被预先分配给IEnumerable<T>之前 GetEnumerator甚至被调用过.虽然这很好,例如 DirectoryNotFoundException和(由用户可能期望的)FileNotFoundException直接抛出的异常 File.ReadLines,但这也意味着如果用户实际上没有超过可枚举的那么读者将被泄露(因此在至少一个IEnumerator<T>实例上调用Dispose ).

因此,File.ReadLines()当传递无效或不可读的路径时,他们想立即抛出,而不是在枚举时抛出.

替代方案很简单:Take(0)如果您对其内容实际上并不感兴趣,则不要调用,或者不完全读取文件.