Fechar e descartar - para quem ligar?


Respostas:


191

Eu quero esclarecer esta situação.

De acordo com as diretrizes da Microsoft, é uma boa prática fornecer o Closemétodo adequado. Aqui está uma citação das diretrizes de design do Framework

Considere fornecer o método Close(), além do Dispose(), se close é uma terminologia padrão na área. Ao fazer isso, é importante que você torne a Closeimplementação idêntica a Dispose...

Na maioria dos casos Closee Disposemétodos são equivalentes. A principal diferença entre Closee Disposeno caso de SqlConnectionObjecté:

Um aplicativo pode ligar Closemais de uma vez. Nenhuma exceção é gerada.

Se você chamou o estado do objeto de Disposemétodo SqlConnection, será redefinido. Se você tentar chamar qualquer método no SqlConnection objeto descartado , receberá uma exceção.

Dito isto:

  • Se você usar o objeto de conexão uma vez, use Dispose.
  • Se o objeto de conexão precisar ser reutilizado, use o Closemétodo

5
@ Chris, a documentação para Close () diz "Ele libera a conexão com o pool de conexões ou fecha a conexão se o pool de conexões estiver desabilitado". Portanto, Close () deve ser suficiente para impedir que o pool de conexões fique transbordando.
David Hammond

@ David David: Você está certo. Estou excluindo meu comentário anterior.
NotMe

3
.Dispose () também libera a conexão de volta ao pool?
precisa

Este é o melhor argumento que li sobre o assunto de uma forma ou de outra em uma década. Ponto excelente.
22617 Michael Erickson

1
Então, ele funciona dessa maneira 1. con.Open() con.Close(); 2 con.Open(); // reuse 3. con.Dispose(); // use one time con.Open(); // error
shaijut

24

Como sempre, a resposta é: depende. Diferentes classes são implementadas de IDisposablemaneiras diferentes, e cabe a você fazer a pesquisa necessária.

Na medida do possível SqlClient, a prática recomendada é fazer o seguinte:

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
}

Você deve ligar Dispose(ou Close*) na conexão! Você não esperar para o coletor de lixo para limpar a sua conexão, isso vai amarrar conexões na piscina até o próximo ciclo de GC (pelo menos). Se você ligar Dispose, não é necessário ligar Closee, como a usingconstrução facilita o manuseio Disposecorreto, não há realmente motivo para ligar Close.

As conexões são agrupadas automaticamente e a ligação Dispose/ Closeconexão não a fecha fisicamente (em circunstâncias normais). Não tente implementar seu próprio pool. SqlClientexecuta limpeza na conexão quando é recuperada do pool (como restaurar o contexto do banco de dados e as opções de conexão).

* se você estiver ligando Close, certifique-se de fazê-lo de maneira segura contra exceções (ou seja, em uma captura ou, finalmente, em um bloco).


Quando você diz: "depende de você fazer a pesquisa necessária", o que é essa pesquisa? A única maneira de saber com certeza é através da Reflexão, mas isso tem o lado ruim de ser "ilegal" na maioria das situações.
Storm

7
Eu não diria: conn.Close(); // Optionalnão é opcional. É redundante e desnecessário. Você está descartando o objeto duas vezes e isso será marcado como um aviso por algumas ferramentas de análise de código.
Metalogic 08/02

@ Metalogic Concordo que é redundante desnecessário (e feio) chamar Close com os usos adequados. No entanto, nitpicking: chamar Close não está "descartando" (enquanto Dispose implica Close para um SqlConnection). Compare com using (var x = ..) { x.Dispose(); }, caso em que xrealmente é "descartado duas vezes".
user2864740

11

Você precisa chamar Dispose ()!

Dispose () é para o desenvolvedor chamar, o Garbage Collector chama Finalize (). Se você não chamar Dispose () em seus objetos, os recursos não gerenciados que eles usaram não serão descartados até que o coletor de lixo chegue e chame a finalização deles (e quem sabe quando isso acontecerá).

Esse cenário é chamado de Finalização não determinística e é uma armadilha comum para desenvolvedores .net. Se você estiver trabalhando com objetos que implementam IDisposable, chame Dispose () neles!

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

Embora possa haver muitas instâncias (como em SqlConnection) em que você chama Disponse () em algum objeto e simplesmente chama Close () em sua conexão ou fecha um identificador de arquivo, quase sempre é sua melhor opção chamar Dispose ()! a menos que você planeje reutilizar o objeto em um futuro próximo.


26
Este comentário é totalmente falso. O coletor de lixo nunca liga Dispose.
Stephen Cleary

3
Corolário: você deve ligar Dispose() se não estiver usando using()uma classe que implementa IDisposable. Se a classe que está sendo chamada implementa IDisposable e você incluiu seu uso na página interna using(), é possível descartar com o Dispose()(trocadilho intencional, então atire em mim). Close()No entanto, é recomendável usar qualquer coisa que utilize explicitamente o Open()AFAIK.
René Kåbis 1/10

Não tenho certeza sobre outros DBMS, mas você NÃO pode fazer as duas coisas no PostgreSql . Depois de Closeconectar-se, o Postgres define automaticamente o identificador de conexão como null. A partir daí, não é possível Disposeum identificador de conexão sql que já esteja definido como null.
Ssd #

10

Pois SqlConnection, da perspectiva da própria conexão, eles são equivalentes. De acordo com o Reflector, Dispose()chamadas Close(), além de realizar algumas operações adicionais de liberação de memória - principalmente configurando membros como nulos.

Para o Stream, eles são realmente equivalentes. Stream.Dispose()simplesmente chama Close ().


1
Você tem certeza? O MSDN diz que é herdado doComponent qual parece não fazer nada para tentar ligarClose() . Não consigo ver nenhum lugar DBConnectionou SqlConnectionque esteja vinculado a nenhuma dessas notificações. No entanto, possui um particular DisposeMe()que não é referenciado em nenhum lugar .
23415 Deanna

@Deanna, ele é substituído aqui: github.com/dotnet/corefx/blob/…
David Cumps

@DavidCumps Parece que mudou nos 4 anos desde que escrevi esse comentário. Meus links não são mais válidos.
Deanna


6

Esse possível conselho rápido tornou-se uma resposta longa. Desculpe.

Como Tyler apontou em sua bela resposta, chamar Dispose()é uma ótima prática de programação. Isso ocorre porque esse método deve "reunir" toda a liberação de recursos necessária para que não haja recursos abertos desnecessários. Se você escreveu algum texto em um arquivo, por exemplo, e não conseguiu fechar o arquivo (liberar o recurso), ele permanecerá aberto e ninguém mais poderá gravá-lo até que o GC chegue e faça o que você deveria feito.

Agora, em alguns casos, haverá métodos de "finalização" mais específicos da classe com a qual você está lidando StreamWriter.Close(), que substitui TextWriter.Close(). De fato, eles geralmente são mais adequados à situação: um StreamWriter Close(), por exemplo, libera o fluxo e o codificador subjacente antes Dispose()da entrada do objeto! Legal!

No entanto, navegando no MSDN, você verá que até a Microsoft às vezes fica confusa com a multidão de closers e trituradores. Nesta página da Web , por exemplo, em alguns exemplos Close()é chamado antes do implícito Dispose()(consulte a instrução using, se você não entende por que está implícito), e em um em particular eles não se preocupam. Por que isso seria? Eu também fiquei perplexo.

A razão pela qual eu imaginei (e, enfatizo, essa é uma pesquisa original e, com certeza, posso perder a reputação se estiver errada) é que Close()pode falhar, gerando uma exceção ao mesmo tempo em que deixa os recursos abertos, e Dispose()certamente os liberaria . É por isso que a Dispose()deve sempre salvaguardar uma Close()ligação (desculpe pelo trocadilho).

MyResource r = new MyResource();

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

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

E sim, acho que a Microsoft escorregou nesse exemplo. Talvez esse carimbo de data e hora nunca seja liberado para o arquivo.

Estou corrigindo meu código antigo amanhã.

Edit: desculpe, Brannon, não posso comentar sua resposta, mas você tem certeza de que é uma boa ideia chamar Close()um finallybloco? Eu acho que uma exceção disso pode arruinar o resto do bloco, o que provavelmente conteria um código de limpeza importante.

Resposta para Brannon's: ótimo, mas não se esqueça de ligar Close()quando for realmente necessário (por exemplo, ao lidar com fluxos - não saiba muito sobre conexões SQL no .NET).


Na verdade, nunca chamo Close (), apenas deixo Dispose () e a construção 'using' fazer a coisa certa . Se você não estiver chamando Dispose, precisará ligar para Close de uma maneira segura para exceções. Pode ser uma boa ideia adicionar manipulação de exceção ao bloco final.
Brannon

Certo, meus comentários foram para o SqlClient especificamente. O ponto é que você precisa entender as classes que está usando. Sempre ligar para Dispose não é necessariamente a resposta certa.
Brannon

2

Typecast para iDisposable e ligue para descartá-lo. Isso invocará qualquer método que esteja configurado para implementar "iDisposable.Dispose", independentemente do nome da função.


A "função é nomeada" 'Dispose': portanto, voltamos à pergunta inicial:}
user2864740 23/11

A função está vinculada IDisposable.Dispose, mas isso não significa que esse seja o nome. Observe que no vb.net é possível vincular uma função a vários membros da interface com nomes que não precisam estar relacionados ao da função.
Supercat

Elenco assim:using (myObj as IDisposable)
Yousha Aleayoub 01/11/19

2

Geralmente, estamos enfrentando problemas em Close (), Abort () e Dispose (), mas deixe-me dizer a diferença entre eles.

1) ABORT: - Não sugiro usar isso porque quando o abort é chamado, o cliente exclui a conexão sem informar o servidor, para que o servidor aguarde um certo período de tempo (aproximadamente 1 minuto). Se você tiver uma solicitação em massa, não poderá usar abort () porque isso pode causar um tempo limite para o seu pool de conexões limitado.

2) Fechar: - Fechar é uma maneira muito boa de fechar a conexão, pois ao fechar a conexão, ele chamará o servidor e reconhecerá que o servidor também fecha por esse lado.

Aqui, mais uma coisa para procurar. Em alguns casos, se o erro é gerado, não é uma boa maneira de escrever código finalmente nessa connection.close () porque, naquele momento, o Estado da Comunicação estará com falha.

3) Descarte: - É um tipo de fechamento, mas depois de fechar a conexão, você não pode abri-la novamente.

Então tente dessa maneira,

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

A verificação client != nullestá incorreta / enganosa porque não protege todos os usos. Além disso, não tenho certeza de como o código pode chegar ao estado "esta conexão não está aberta e deve ser fechada".
user2864740
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.