Cerrar y desechar: ¿a cuál llamar?

Respuestas:

191

Quiero aclarar esta situación.

De acuerdo con las pautas de Microsoft, es una buena práctica proporcionar un Closemétodo cuando sea adecuado. Aquí hay una cita de las pautas de diseño de Framework

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

En la mayoría de casos Closey Disposemétodos son equivalentes. La principal diferencia entre Closey Disposeen el caso de SqlConnectionObjectes:

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

Si llamó al Disposemétodo, el SqlConnectionestado del objeto se restablecerá. Si intenta llamar a cualquier método en un SqlConnection objeto desechado , 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, utilice el Closemétodo
aku
fuente
55
@Chris, la documentación de Close () dice "Luego libera la conexión al grupo de conexiones o cierra la conexión si la agrupación de conexiones está deshabilitada". Entonces Close () debería ser suficiente para evitar que el grupo de conexiones se desborde.
David Hammond
@DavidHammond: Tienes razón. Estoy borrando mi comentario anterior.
NotMe
3
¿.Dispose () también libera la conexión de vuelta al grupo?
oscilatingcretin
Este es el mejor argumento que he leído sobre el tema de una forma u otra en una década. Excelente punto
Michael Erickson
1
Así funciona de esta manera 1. con.Open() con.Close(); 2 con.Open(); // reuse 3. con.Dispose(); // use one time con.Open(); // error
shaijut
24

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

Hasta donde SqlClientllega, 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ías estar llamando Dispose(o Close*) en la conexión! No , no esperar a que el recolector de basura para limpiar su conexión, esto va a atar a las conexiones en la piscina hasta el siguiente ciclo de GC (por lo menos). Si llama Dispose, no es necesario llamar Close, y dado que la usingconstrucción hace que sea tan fácil de manejar Disposecorrectamente, realmente no hay razón para llamar Close.

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

* si está llamando Close, asegúrese de hacerlo de manera segura (es decir, en una captura o finalmente bloquear).

Brannon
fuente
Cuando dices, "depende de ti hacer la investigación necesaria", ¿qué es esa investigación? La única forma en que sé decir con certeza es a través de Reflexión, pero eso tiene el inconveniente de ser "ilegal" en la mayoría de las situaciones.
Tormenta el
77
Yo no diría: conn.Close(); // Optionalno es opcional. Es redundante e innecesario. Desecha el objeto dos veces y algunas herramientas de análisis de código lo marcarán como advertencia.
Metalogic
@Metalogic Estoy de acuerdo en que es redundante e innecesario (y feo) llamar a Close con los usos adecuados. Sin embargo, nitpicking: llamar a Close no es "deshacerse" (mientras que Dispose implica Close para una conexión Sql). Compare con using (var x = ..) { x.Dispose(); }, en cuyo caso xrealmente está "dispuesto dos veces".
usuario2864740
11

¡Necesitas 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 basura se acerque y las llamadas finalicen (y quién sabe cuándo sucederá eso).

Este escenario se llama Finalización no determinista y es una trampa común para los desarrolladores de .net. Si está trabajando con objetos que implementan IDisposable, ¡llame a Dispose () en 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 llama a Disponse () en algún objeto y simplemente llama a Cerrar () en su conexión o cierra un identificador de archivo, ¡ casi siempre es su mejor opción llamar a Dispose ()! a menos que planee reutilizar el objeto en un futuro muy cercano.

Tyler
fuente
26
Este comentario es totalmente falso. El recolector de basura nunca, nunca llama Dispose.
Stephen Cleary
3
Corolario: debe llamar Dispose() si no está utilizando using()una clase que implementa IDisposable. Si la clase que se llama implementa IDisposable y ha incluido su uso en la página using(), puede deshacerse de él Dispose()(juego de palabras, así que dispare). El uso Close(), sin embargo, se recomienda con cualquier cosa que utiliza de forma explícita Open(), que yo sepa.
René Kåbis el
No estoy seguro acerca de otros DBMS, pero NO puede hacer ambas cosas en PostgreSql . Una vez que tiene Closeuna conexión, Postgres establece automáticamente el identificador de conexión en null. A partir de ahí, no se puede Disposeun identificador de conexión SQL que ya esté configurado en null.
SSD
10

Porque SqlConnection, desde la perspectiva de la conexión misma, son equivalentes. Según Reflector, las Dispose()llamadas Close()además de realizar algunas operaciones adicionales de liberación de memoria, principalmente al establecer miembros iguales a nulos.

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

Curt Hagenlocher
fuente
1
¿Estás seguro? MSDN dice que es heredado deComponent lo que no parece hacer nada para intentar llamarClose() . No puedo ver en ninguna parte DBConnectionni SqlConnectioneso se relaciona con ninguna de esas notificaciones. Sin embargo, tiene un privado al DisposeMe()que no se hace referencia en ninguna parte .
Deanna
@Deanna se anula aquí: github.com/dotnet/corefx/blob/…
David Cumps
@DavidCumps Parece que ha cambiado en los 4 años desde que escribí ese comentario. Mis enlaces ya no son válidos.
Deanna
6

Este posible consejo rápido se convirtió en una respuesta larga. Lo siento.

Como señaló Tyler en su buena respuesta, llamar Dispose()es una gran práctica de programación. Esto se debe a que se supone que este método "reúne" toda la liberación de recursos necesaria para que no haya recursos abiertos innecesarios. Si escribió un texto en un archivo, por ejemplo, y no pudo cerrar el archivo (libere el recurso), permanecerá abierto y nadie más podrá escribir en él hasta que aparezca el GC y haga lo que debe hacer. 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, por lo general son más adecuados para la situación: un StreamWriter Close(), por ejemplo, descarga el flujo y el codificador subyacente antes Dispose()del objeto. ¡Frio!

Sin embargo, al navegar por MSDN encontrará que incluso Microsoft a veces se confunde por la multitud de cerradores y trituradores. En esta página web , por ejemplo, en algunos ejemplos Close()se llama antes de lo implícito Dispose()(vea el uso de la declaración si no entiende por qué es 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, esta es una investigación original y seguramente podría perder reputación si me equivoco) es que Close()podría fallar, produciendo una excepción al dejar recursos abiertos, mientras Dispose()que seguramente los liberaría . Es por ello que una Dispose()siempre debe salvaguardar una Close()llamada (perdón por el juego de palabras).

MyResource r = new MyResource();

try {
  r.Write(new Whatever());

  r.Close()
finally {
  r.Dispose();
}

Y sí, supongo que Microsoft resbaló en ese ejemplo. Quizás esa marca de tiempo nunca se volcaría en el archivo.

Estoy arreglando mi viejo código mañana.

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

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

André Chalella
fuente
En realidad, nunca llamo a Close (), solo dejo que Dispose () y la construcción 'using' haga lo correcto . Si no está llamando a Dispose, debe llamar a Close de manera segura. Puede ser una buena idea agregar manejo de excepciones al bloque finalmente.
Brannon
Correcto, mis comentarios fueron específicamente para SqlClient. El punto es que necesitas entender las clases que estás usando. Siempre llamar a Dispose no es necesariamente la respuesta correcta.
Brannon
2

Typecast a iDisposable, y llame a dispose en eso. Eso invocará cualquier método configurado como implementación "iDisposable.Dispose", independientemente de cómo se llame la función.

Super gato
fuente
La "función se llama" 'Eliminar': así que volvemos a la pregunta inicial:}
user2864740
La función está vinculada IDisposable.Dispose, pero eso no significa que ese sea el nombre. Tenga en cuenta que en vb.net, es posible tener una función vinculada a múltiples miembros de la interfaz con nombres que no necesitan estar relacionados con el de la función.
supercat
Reparto como este:using (myObj as IDisposable)
Yousha Aleayoub
2

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

1) ABORTAR: - No sugeriré usar esto porque cuando se llama a abortar, el cliente eliminará la conexión sin decirle al servidor, por lo que el servidor esperará una cierta cantidad de tiempo (aproximadamente 1 minuto). Si tiene una solicitud masiva, no puede usar abort () porque puede causar un tiempo de espera para su grupo de conexiones limitado.

2) Cerrar: - Cerrar es una muy buena manera de cerrar la conexión porque al cerrar la conexión llamará al servidor y confirmará que el servidor también se cerrará 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 finalmente en esa conexión.close () porque en ese momento se producirá un error en el estado de comunicación.

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

Así que intenta de esta manera,

private void CloseConnection(Client client)
    {
        if (client != null && client.State == CommunicationState.Opened)
        {
            client.Close();
        }
        else
        {
            client.Abort();
        }
    }
Jadia profunda
fuente
La verificación client != nulles incorrecta / engañosa porque no protege todos los usos. Además, no estoy seguro de cómo el código puede llegar al estado de "esta conexión no está abierta y debería cerrarse".
usuario2864740