When does File.ReadLines free resources
+1 for Lasse's answer.
Particularly for File.ReadLines
where the enumerator calls .MoveNext()
the internal TextReader
will be disposed of when it encounters an EOF, or if a fault occurs.
private bool MoveNext()
{
bool flag;
try
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>7__wrap2 = this.reader;
this.<>1__state = 1;
this.<line>5__1 = null;
goto Label_005C;
case 2:
this.<>1__state = 1;
goto Label_005C;
default:
goto Label_0078;
}
Label_003E:
this.<>2__current = this.<line>5__1;
this.<>1__state = 2;
return true;
Label_005C:
if ((this.<line>5__1 = this.reader.ReadLine()) != null)
{
goto Label_003E;
}
this.<>m__Finally3(); // Disposal at end of file.
Label_0078:
flag = false;
}
fault
{
this.System.IDisposable.Dispose(); // Disposal due to fault.
}
return flag;
}
IEnumerable
doesn't inherit from IDisposable because typically, the class that implements it only gives you the promise of being enumerable, it hasn't actually done anything yet that warrants disposal.
However, when you enumerate over it, you first retrieve an IEnumerator
by calling the IEnumerable.GetEnumerator
method, and typically, the underlying object you get back does implement IDisposable
.
The way foreach
is implemented is similar to this:
var enumerator = enumerable.GetEnumerator();
try
{
// enumerate
}
finally
{
IDisposable disposable = enumerator as IDisposable;
if (disposable != null)
disposable.Dispose();
}
This way, if the object does indeed implement IDisposable
, it will be disposed of. For File.ReadLines
, the file isn't really opened until you start enumerating over it, so the object you get from File.ReadLines
doesn't need disposing, but the enumerator you get, does.
As the comments indicate, IEnumerator
does not inherit from IDisposable
, even though many typical implementations does, whereas the generic IEnumerator<T>
does inherit IDisposable
.