unity c# generics ienumerator

unity - ienumerable c#



¿Por qué IEnumerator<T> hereda de IDisposable mientras que el IEnumerator no genérico no? (6)

¿IEnumerable` hereda IDisposing? De acuerdo con el reflector .NET o MSDN . ¿Estás seguro de que no lo estás confundiendo con IEnumerator ? Eso usa IDisposing porque es solo para enumerar una colección y no para longevidad.

Noté que el IEnumerator<T> genérico IEnumerator<T> hereda de IDisposable, pero la interfaz no genérica IEnumerator no lo hace. ¿Por qué está diseñado de esta manera?

Por lo general, usamos la declaración foreach para pasar por una IEnumerator<T> . El código generado de foreach en realidad tiene un bloque try-finally que invoca a Dispose () finalmente.


Básicamente fue un descuido. En C # 1.0, foreach nunca llamó a Dispose 1 . Con C # 1.2 (introducido en VS2003 - no hay 1.1, extrañamente) foreach comenzó a verificar en el bloque IDisposable si el iterador implementaba IDisposable - tenían que hacerlo de esa manera, porque hacer IEnumerator extensible de forma IDisposable habría roto la implementación de todos de IEnumerator . Si hubieran descubierto que es útil para foreach disponer de iteradores en primer lugar, estoy seguro de que IEnumerator habría extendido IDisposable .

Sin embargo, cuando aparecieron C # 2.0 y .NET 2.0, tuvieron una nueva oportunidad: nueva interfaz, nueva herencia. Tiene mucho más sentido que la interfaz se extienda IDisposable para que no necesite una verificación en tiempo de ejecución en el bloque finally, y ahora el compilador sabe que si el iterador es un IEnumerator<T> puede emitir una llamada incondicional a Dispose

EDITAR: es increíblemente útil que se llame a Dispose al final de la iteración (como sea que termine). Significa que el iterador puede conservar los recursos, lo que hace que sea factible, por ejemplo, leer un archivo línea por línea. Generador de bloques de iterador Dispose implementaciones que aseguren que los bloques finally relevantes al "punto de ejecución actual" del iterador se ejecuten cuando se eliminen, por lo que puede escribir código normal dentro del iterador y la limpieza debe realizarse de forma adecuada.

1 Mirando hacia atrás en la especificación 1.0, ya estaba especificado. Todavía no he podido verificar esta afirmación anterior de que la implementación 1.0 no llamó a Dispose .


Es un poco difícil ser definitivo en esto, a menos que logre obtener una respuesta del propio AndersH, o de alguien cercano a él.

Sin embargo, creo que se relaciona con la palabra clave "rendimiento" que se introdujo en C # al mismo tiempo. Si observa el código generado por el compilador cuando se usa "yield return x", verá el método envuelto en una clase auxiliar que implementa IEnumerator; al hacer que IEnumerator desciende de IDisposable, se asegura de que se pueda limpiar cuando se complete la enumeración.


IEnumerable <T> no hereda IDisposable. Sin embargo, IEnumerator <T> hereda IDisposable, mientras que el IEnumerator no genérico no lo hace. Incluso cuando utiliza foreach para un IEnumerable no genérico (que devuelve IEnumerator), el compilador generará una verificación de IDisposable y llamará a Dispose () si el enumerador implementa la interfaz.

Supongo que el Enumerator genérico <T> hereda de IDisposable, por lo que no es necesario que sea un tipo de tiempo de ejecución check-it; puede seguir y llamar a Dispose (), que debería tener un mejor rendimiento, ya que probablemente se pueda optimizar si el el enumerador tiene un método Dispose () vacío.


IIRC Todo el asunto de tener IEnumerable<T> e IEnumerable es el resultado de IEnumerable .Net''s template stuff. Sospecho que tu pregunta es de la misma manera.


Sé que esta es una discusión antigua, pero escribí una biblioteca con bastante razonamiento, donde utilicé IEnumerable de T / IEnumerator of T, donde los usuarios de la biblioteca podrían implementar iteradores personalizados, deberían implementar IEnumerator of T.

Me pareció muy extraño que IEnumerator of T heredara de IDisposable. Implementamos IDisposable si queremos liberar recursos no manchados ¿no? Por lo tanto, solo sería relevante para los enumeradores que realmente tienen recursos no administrados, como una secuencia IO, etc. ¿Por qué no solo permite que los usuarios implementen tanto IEnumerator of T como IDisposable en su enumerador si tiene sentido? En mi libro, esto viola el principio de responsabilidad única: por qué mezclar la lógica del enumerador y eliminar los objetos.