我有一个函数,它使用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)Cod*_*ter 18
从File.ReadLines()参考资料中的上述评论中可以看出,负责团队知道这个"错误":
无法更改以保持与4.0兼容的已知问题:
- 底层
StreamReader被预先分配给IEnumerable<T>之前GetEnumerator甚至被调用过.虽然这很好,例如DirectoryNotFoundException和(由用户可能期望的)FileNotFoundException直接抛出的异常File.ReadLines,但这也意味着如果用户实际上没有超过可枚举的那么读者将被泄露(因此在至少一个IEnumerator<T>实例上调用Dispose ).
因此,File.ReadLines()当传递无效或不可读的路径时,他们想立即抛出,而不是在枚举时抛出.
替代方案很简单:Take(0)如果您对其内容实际上并不感兴趣,则不要调用,或者不完全读取文件.