在返回IEnumerable <T>的方法中使用IDisposable对象
想象一下,你有一个内部使用IDisposable对象的方法(例如streamreader),yield从文件中读取返回的项目。 喜欢这个:
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); } } } }
当我使用不迭代完整集合的LINQ方法时, reader
和文件filestream
会被处理吗?
YourOjbect firstLine = Read("myfile.txt").First();
当你使用yield
关键字编译器生成的嵌套类,它实现了IEnumerable
, IEnumerator
和IDisposable
并存储所有的上下文数据:
[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; } }
正如你所看到的,来自yielding方法的上下文的所有局部variables都移到了这个生成的类的字段中。 有趣的方法是那些最后有名字的方法:
private void <>m__Finally4() { this.<>1__state = -1; if (this.<filestream>5__1 != null) { this.<filestream>5__1.Dispose(); } }
正如你所看到的,这些方法处理你的一次性对象( FileStream
和StreamReader
)。 当被叫? 在枚举结束时,或者在调用Dispose
时:
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; } }
如果你看看Enumerable
Fist()
实现,那么你会看到 – 它返回第一个项目后调用Dispose
:
using (IEnumerator<TSource> enumerator = source.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } }
因此,将调用自动生成的类的Dispose
,并且所有需要处理的局部variables将通过调用m_Finally
方法来m_Finally
。
顺便说一句(不是关于与LINQ的使用)如果你看看foreach语句的实现,你会看到枚举器在枚举之后被处理。 因此,即使在break
或exception的情况下,也会调用生成的类的Dispose
。
是的,他们被处置。
[编辑]只要你使用LINQ方法或foreach循环,处理将自动处理。 但是,如果您决定手动调用.Enumerator().MoveNext()
方法,则需要.Enumerator().MoveNext()
处理。 [/编辑]
此代码:
class something : IDisposable { public void Dispose() { Console.WriteLine("Disposing"); Console.WriteLine(Environment.StackTrace); } } static IEnumerable<string> ie() { using (new something()) { Console.WriteLine("first"); yield return "first"; Console.WriteLine("second"); yield return "second"; } } static void Main(string[] args) { Console.WriteLine("before"); ie().First(); Console.WriteLine("after"); }
打印:
before first Disposing at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo) at System.Environment.get_StackTrace() at TestApp.Program.something.Dispose() in C:\Users\Tim\Documents\Visual Studi o 2010\Projects\TestApp\TestApp\Program.cs:line 198 at TestApp.Program.<ie>d__0.<>m__Finally2() in C:\Users\Tim\Documents\Visual Studio 2010\Projects\TestApp\TestApp\Program.cs:line 0 at TestApp.Program.<ie>d__0.System.IDisposable.Dispose() in C:\Users\Tim\Docu ments\Visual Studio 2010\Projects\TestApp\TestApp\Program.cs:line 0 at System.Linq.Enumerable.First[TSource](IEnumerable`1 source) at TestApp.Program.Main(String[] args) in C:\Users\Tim\Documents\Visual Studi o 2010\Projects\TestApp\TestApp\Program.cs:line 214 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args ) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySec urity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C ontextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C ontextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() after
这是一个常见的LINQ问题/问题,是的 – LINQ将在执行时处理它获得的所有一次性元素。