I know that when using the IEnumerable
returned by File.ReadLines()
in a foreach
loop, the file gets closed automatically after the loop.
I just need to quickly check the first line of a file. Is this enough or will it keep the file open?
protected void Append(string filePath, Encoding encoding)
{
try
{
string firstLine = File.ReadLines(filePath, encoding).First();
// more code here
}
catch
{
// more code here
}
}
(Note that this is for File.ReadLines()
which returns an IEnumerable<String>
- this is not for File.ReadAllLines()
which returns a String[]
.)
Does
File.ReadLines(filePath).First()
close the file immediately?
...assuming by "immediately" you mean when the entire statement completes - not just the inner ReadLines()
sub-expression.
File.ReadLines()
returns an instance of ReadLinesIterator
- which is an IEnumerable<T>
.IEnumerable<T>
is iterated-over, C#/.NET uses IEnumerable<T>.GetEnumerator<T>()
which returns an IEnumerator<T>
which must be disposed after the program has finished iterating what it wants.
IEnumerator<T>
instances must be disposed you're always encouraged to use foreach
which handles this for you (instead of manually handling an IEnumerator<T>
yourself).
foreach
will also ensure the IEnumerator<T>
is disposed if an exception is thrown inside the foreach
loop body.ReadLinesIterator
contains a StreamReader
(which contains the open FileStream
). When the ReadLinesIterator
is disposed the internal StreamReader
is closed, which in-turn closes the FileStream
..Frist()
method is Linq's Enumerable.First( IEnumerable<T> source )
.
First()
does the same thing as calling foreach( T item in source )
and returning immediately inside the foreach - so the First
method will dispose of the ReadLinesIterator
for you.I note that ReadLinesIterator
is both an IEnumerator<T>
and an IEnumerable<T>
and it wraps an open StreamReader
- which does mean that you do need to be careful when using ReadLines()
to ensure that the IEnumerable<T>
you see is actually iterated-over, otherwise you do risk leaking open file handles.
...this also means that if you're using a Linq method chain and an exception happens inside the Linq chain but outside any of Linq's internal try/catch
/foreach
/using
blocks then you will leak an file handle that won't be closed until the GC finalizes the ReadLinesIterator
... though I admit I struggle to think of when this could happen.