waitforseconds que c# .net exception using yield-return

que - yield return c# stack overflow



Rendimiento de rendimiento dentro de los usos (2)

El compilador debe manejar el yield dentro del bloque de using correctamente en ambos casos. No hay una razón obvia por la que debería lanzar una excepción.

Una cosa a tener en cuenta es que la conexión solo se eliminará una vez que complete la iteración y / o elimine manualmente el objeto del enumerador. Si expone este código en un método público, entonces es posible que un código estúpido o malicioso pueda mantener su conexión abierta durante mucho tiempo:

var enumerable = YourMethodThatYieldsFromTheDataReader(); var enumerator = enumerable.GetEnumerator(); enumerator.MoveNext(); Thread.Sleep(forever); // your connection will never be disposed

Si recuerdo correctamente, cuando utilicé el rendimiento dentro de bloques using SqlConnection obtuve excepciones de tiempo de ejecución.

using (var connection = new SqlConnection(connectionString)) { var command = new SqlCommand(queryString, connection); connection.Open(); SqlDataReader reader = command.ExecuteReader(); // Call Read before accessing data. while (reader.Read()) { yield reader[0]; } // Call Close when done reading. reader.Close(); }

Esos problemas se resolvieron cuando reemplacé el yield por una lista donde agregué elementos en cada iteración.

El mismo problema no me pasó cuando estaba using StreamReader bloques de using StreamReader

using (var streamReader = new StreamReader(fileName)) { string line; while ((line = streamReader.ReadLine()) != null) { yield return line; } }

¿Hay alguna explicación de por qué las Excepciones ocurrieron en el primer caso y no en el segundo? ¿Es aconsejable esta construcción?

EDITAR Para obtener el error (eliminación anticipada) que hice en el pasado, debe llamar al primer método a continuación:

IEnumerable<string> Read(string fileName) { using (var streamReader = new StreamReader(fileName)) { return Read(streamReader); } // Dispose will be executed before ReadLine() because of deffered execution } IEnumerable<string> Read(StreamReader streamReader) { string line; while ((line = streamReader.ReadLine()) != null) { yield return line; } }

Se puede lograr el mismo error con otras formas de diferir la ejecución, como System.Linq.Enumerable.Select()


Vea esta publicación para una buena explicación de los problemas con el using y el yield . Como regresas en el enumerador, el bloque de uso ya habrá destruido el contexto antes de acceder a cualquier cosa. Las respuestas tienen buenas soluciones, básicamente, o bien hacen que el método wrapper sea un enumerador, o crean una lista en su lugar.

También suele ser más práctico using el lector, no la conexión, y usar CommandBehavior.CloseConnection para garantizar que los recursos se liberen cuando el lector termine. Aunque realmente no importa en su situación, si alguna vez devuelve un lector de datos de un método, esto asegurará que la conexión se cierre correctamente cuando se deseche el lector.

using(SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection)) { while (reader.Read()) { yield reader[0]; } }