Cerrar y desechar: ¿a qué llamar?

Después de leer los hilos ¿Es SqlCommand.Desea lo suficiente? y Cerrando y Desechando un Servicio WCF Me pregunto por clases como SqlConnection o una de las varias clases heredadas de la clase Stream ¿Importa si cierro Dispose en vez de Cerrar?

Quiero aclarar esta situación.

De acuerdo con las directrices de Microsoft, es una buena práctica proporcionar el método Close cuando sea adecuado. Aquí hay una cita de las pautas de diseño del Marco

Considere proporcionar el método Close() , además de Dispose() , si close es terminología estándar en el área. Al hacerlo, es importante que haga que la implementación de Close idéntica a Dispose

En la mayoría de los casos, los métodos de Close y Dispose son equivalentes. La principal diferencia entre Close y Dispose en el caso de SqlConnectionObject es:

Una aplicación puede llamar Close más de una vez. No se genera ninguna excepción.

Si llamó al método Dispose , se restablecerá el estado del objeto SqlConnection . Si intenta llamar a cualquier método en el objeto SqlConnection eliminado, recibirá una excepción.

Eso dijo:

  • Si usa el objeto de conexión una vez, use Dispose .
  • Si el objeto de conexión debe reutilizarse, use el método Close .

Como de costumbre, la respuesta es: depende. Diferentes clases implementan IDisposable de diferentes maneras, y depende de usted hacer la investigación necesaria.

En lo que respecta a SqlClient , la práctica recomendada es hacer lo siguiente:

 using (SqlConnection conn = /* Create new instance using your favorite method */) { conn.Open(); using (SqlCommand command = /* Create new instance using your favorite method */) { // Do work } conn.Close(); // Optional } 

¡ Debería llamar a Dispose (o Close *) en la conexión! No espere a que el recolector de basura limpie su conexión, esto atará las conexiones en el grupo hasta el próximo ciclo de GC (al menos). Si llama a Dispose , no es necesario llamar a Close , y dado que el constructo using hace que sea tan fácil de manejar Dispose correctamente, realmente no hay ninguna razón para llamar a Close .

Las conexiones se agrupan automáticamente y llamar a Dispose / Close en la conexión no cierra físicamente la conexión (en circunstancias normales). No intente implementar su propia agrupación. SqlClient realiza la limpieza de la conexión cuando se recupera del grupo (como la restauración del contexto de la base de datos y las opciones de conexión).

* si llama a Close , asegúrese de hacerlo de una manera segura (es decir, en un bloque catch o finally).

¡DEBES llamar a Dispose ()!

Dispose () es para que el desarrollador llame, el recolector de basura llama a Finalize (). Si no llama a Dispose () en sus objetos, los recursos no administrados que usaron no se eliminarán hasta que el recolector de elementos no utilizados aparezca y finalice (y quién sabe cuándo sucederá).

Este escenario se denomina Finalización no determinista y es una trampa común para los desarrolladores .net. Si está trabajando con objetos que implementan IDisposable, ¡llame a Dispose () sobre ellos!

http://www.ondotnet.com/pub/a/oreilly/dotnet/news/programmingCsharp_0801.html?page=last

Si bien puede haber muchas instancias (como en SqlConnection) donde se llama Disponse () en algún objeto y simplemente llama a Close () en su conexión o cierra un identificador de archivo, casi siempre es su mejor opción llamar a Dispose (). a menos que planees reutilizar el objeto en un futuro muy cercano.

Para SqlConnection , desde la perspectiva de la conexión en sí, son equivalentes. De acuerdo con Reflector, Dispose() llama a Close() y también realiza algunas operaciones adicionales de liberación de memoria, principalmente estableciendo los miembros como nulos.

Para Stream, en realidad son equivalentes. Stream.Dispose() simplemente llama a Close ().

Este consejo rápido sería una respuesta larga. Lo siento.

Como Tyler señaló en su agradable respuesta, llamar a Dispose() es una gran práctica de progtwigción. Esto se debe a que se supone que este método “reúne” todos los recursos necesarios para que no existan recursos abiertos innecesarios. Si escribió algo de texto en un archivo, por ejemplo, y no cerró el archivo (libere el recurso), permanecerá abierto y nadie más podrá escribir hasta que aparezca el GC y haga lo que debería. hecho.

Ahora, en algunos casos, habrá métodos de “finalización” más específicos para la clase con la que está tratando, como StreamWriter.Close() , que anula TextWriter.Close() . De hecho, suelen ser más adecuados para la situación: un StreamWriter’s Close() , por ejemplo, vacía la secuencia y el codificador subyacente antes de Dispose() ing del objeto. ¡Guay!

Sin embargo, al explorar MSDN, encontrará que incluso a veces Microsoft se confunde por la multitud de cerradores y trituradores. En esta página web , por ejemplo, en algunos ejemplos se Close() antes del Dispose() implícito Dispose() (ver using statement si no entiendes por qué está implícito), y en uno en particular no se molestan. ¿Por qué sería eso? Yo también estaba perplejo.

La razón por la que pensé (y, enfatizo, esto es investigación original y seguramente perderé reputación si estoy equivocado) es que Close() podría fallar, produciendo una excepción al dejar recursos abiertos, mientras que Dispose() seguramente los liberaría . Por eso, un Dispose() siempre debe salvaguardar una llamada Close() (perdón por el juego de palabras).

 MyResource r = new MyResource(); try { r.Write(new Whatever()); r.Close() finally { r.Dispose(); } 

Y sí, creo que Microsoft se deslizó en ese único ejemplo. Tal vez esa marca de tiempo nunca se tiraría al archivo.

Estoy arreglando mi código anterior mañana.

Edit: lo siento, Brannon, no puedo comentar tu respuesta, pero ¿estás seguro de que es una buena idea llamar a Close() en un bloque finally ? Supongo que una excepción podría arruinar el rest del bloque, que probablemente contenga un código de limpieza importante.

Responda a Brannon’s: genial, simplemente no olvide llamar a Close() cuando realmente lo necesite (por ejemplo, cuando se trata de transmisiones, no sé mucho sobre las conexiones SQL en .NET).

En general, estamos enfrentando problemas en Cerrar (), Abortar () y Descartar (), pero déjame decirte la diferencia entre ellos.

1) ABORTO: – No sugeriré usar esto porque cuando se invoca abortar, el cliente eliminará la conexión sin avisarle al servidor, por lo que el servidor esperará durante un tiempo (aproximadamente 1 minuto). Si tiene una solicitud masiva, entonces no puede usar abort () porque puede causar un tiempo de espera para su grupo de conexiones limitado.

2) Cerrar: – Cerrar es una muy buena forma de cerrar la conexión porque al cerrar la conexión llamará al servidor y confirmará que el servidor también se cierre por ese lado.

Aquí, una cosa más para mirar. En algunos casos, si se genera un error, entonces no es una buena forma de escribir código en esa conexión.close () porque en ese momento se producirá una falla en el estado de comunicación.

3) Eliminar: – Es un tipo de cierre pero después de cerrar la conexión no puede volver a abrirla.

Así que intente de esta manera,

 private void CloseConnection(Client client) { if (client != null && client.State == CommunicationState.Opened) { client.Close(); } else { client.Abort(); } } 

Typecast a iDisposable, y llamada a deshacerse de eso. Esto invocará cualquier método que esté configurado como implementación de “iDisposable.Dispose”, independientemente de cómo se llame la función.