polimorfico objetos objeto net metodos metodo ejemplos ejemplo destruir como clases clase c# linq resources

c# - objetos - Usar un objeto IDisposable en el método que devuelve IEnumerable<T>



ejemplo de un constructor en c# (3)

Imagine que tiene un método que usa internamente un objeto IDisposable (por ejemplo, un lector de secuencias), y cede los elementos devueltos a medida que se leen del archivo. Me gusta esto:

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); } } } }

¿Se eliminarán el reader y el filestream cuando utilizo los métodos LINQ que no iteran en la colección completa?

YourOjbect firstLine = Read("myfile.txt").First();


Cuando utiliza el compilador de palabras clave de yield genera clases anidadas, que implementa IEnumerable , IEnumerator e IDisposable y almacena todos los datos de contexto:

[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; } }

Como puede ver, todas las variables locales desde el contexto del método de rendimiento se movieron a los campos de esta clase generada. Los métodos interesantes son aquellos que tienen m_Finally en sus nombres:

private void <>m__Finally4() { this.<>1__state = -1; if (this.<filestream>5__1 != null) { this.<filestream>5__1.Dispose(); } }

Como puede ver, estos métodos disponen de sus objetos desechables ( FileStream y StreamReader ). Cuando el llamado? Al final de la enumeración, o cuando se llama 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; } }

Si observa la implementación de Enumerable de Fist() , verá que llama a Dispose después de devolver el primer elemento:

using (IEnumerator<TSource> enumerator = source.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } }

Por lo tanto, se llamará a Dispose of auto-generated class, y todas las variables locales, que requieren eliminación, se eliminarán mediante llamadas a m_Finally methods.

Por cierto (no sobre el uso con LINQ) si observas la implementación de la declaración foreach , verás que el enumerador se elimina después de la enumeración. Por lo tanto, se llamará a Dispose en la clase generada incluso en caso de break o excepción.


Este es un problema / pregunta general de LINQ, y sí, cuando se ejecuta, LINQ eliminará todos los elementos desechables que obtenga.


Sí, están dispuestos.

[Editar] Mientras utilice los métodos LINQ o los bucles foreach, la eliminación se realiza automáticamente. Sin embargo, si decides llamar manualmente a .Enumerator().MoveNext() en el método, entonces debes encargarte de la eliminación. [/Editar]

Este código:

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"); }

Huellas dactilares:

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