Using IDisposable object in method that returns IEnumerable<T>
c#, linq, resources
Solution
When you use `yield` keyword compiler generates nested class, which implements `IEnumerable`, `IEnumerator` and `IDisposable` and stores all context data:
[CompilerGenerated]
private sealed class <Read>d__0 : IEnumerable<YourObject>, IEnumerable, IEnumerator<YourObject>, IEnumerator, IDisposable
{
// Fields
private int <>1__state;
private YourObject <>2__current;
public string <>3__filename;
public Foo <>4__this;
private int <>l__initialThreadId;
public FileStream <filestream>5__1;
public string <line>5__3;
public StreamReader <reader>5__2;
public string filename;
// Methods
[DebuggerHidden]
public <Read>d__0(int <>1__state);
private void <>m__Finally4();
private void <>m__Finally5();
private bool MoveNext();
[DebuggerHidden]
IEnumerator<YourObject> IEnumerable<YourObject>.GetEnumerator();
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator();
[DebuggerHidden]
void IEnumerator.Reset();
void IDisposable.Dispose();
// Properties
YourObject IEnumerator<YourObject>.Current { [DebuggerHidden] get; }
object IEnumerator.Current { [DebuggerHidden] get; }
}
As you can see, all local variables from context of the yielding method are moved to fields of this generated class. Interesting methods are those which have `m_Finally` in their names:
private void <>m__Finally4()
{
this.<>1__state = -1;
if (this.<filestream>5__1 != null)
{
this.<filestream>5__1.Dispose();
}
}
As you can see, these methods dispose your disposable objects (`FileStream` and `StreamReader`). When the called? At the end of enumerating, or when `Dispose` is called:
private bool MoveNext()
{
bool CS$1$0000;
try
{
int CS$4$0001 = this.<>1__state;
if (CS$4$0001 != 0)
{
if (CS$4$0001 != 3)
{
goto Label_00AB;
}
goto Label_0074;
}
this.<>1__state = -1;
this.<filestream>5__1 = new FileStream(this.filename, FileMode.Open);
this.<>1__state = 1;
this.<reader>5__2 = new StreamReader(this.<filestream>5__1);
this.<>1__state = 2;
while ((this.<line>5__3 = this.<reader>5__2.ReadLine()) != null)
{
this.<>2__current = new YourObject(this.<line>5__3);
this.<>1__state = 3;
return true;
Label_0074:
this.<>1__state = 2;
}
this.<>m__Finally5();
this.<>m__Finally4();
Label_00AB:
CS$1$0000 = false;
}
fault
{
this.System.IDisposable.Dispose();
}
return CS$1$0000;
}
void IDisposable.Dispose()
{
switch (this.<>1__state)
{
case 1:
case 2:
case 3:
try
{
switch (this.<>1__state)
{
case 2:
case 3:
break;
default:
break;
}
try
{
}
finally
{
this.<>m__Finally5();
}
}
finally
{
this.<>m__Finally4();
}
break;
}
}
If you look to `First()` implementation of `Enumerable`, then you'll see - it calls `Dispose` after returning first item:
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
{
return enumerator.Current;
}
}
Thus `Dispose` of auto-generated class will be called, and all local variables, which require disposing will be disposed by calls to `m_Finally` methods.
BTW (not about usage with LINQ) if you look at foreach statement implementation you'll see that enumerator is disposed after enumerating. Thus `Dispose` on generated class will be called even in case of `break` or exception.
Problem
Imagine you have a method that internally uses an IDisposable object (for example a streamreader), and yield returns items as they are read from the file. Like this: ``` public IEnumerable<YourObject> Read(string filename) { using(var filestream = new FileStream(filename, FileMode.Open)) { using(var reader = new StreamReader(filestream)) { string line; while((line = reader.ReadLine()) != null) { yield return new YourObject(line); } } } } ``` Will the `reader` and the `filestream` be disposed when I use LINQ-methods that doesn't iterate the complete collection? ``` YourOjbect firstLine = Read("myfile.txt").First(); ```