O que acontece se eu retornar antes do final da instrução using? O dispose será chamado?

115

Tenho o seguinte código

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

O dispose()método é chamado no final das usingchaves de instrução, }certo? Já que eu returnantes do final do usingenunciado, o MemoryStreamobjeto será descartado corretamente? o que acontece aqui?

NLV
fonte
4
@JonH: Encontre a duplicata exata e vote para fechar nesse caso, por favor.
Noldorin
@Noldorin: Procurei um cretino nisto, porque imaginei que já devia ter sido perguntado antes, mas não consegui encontrar um. Acho que ainda há perguntas fáceis por aí. :)
Randolpho
@JonH e @Noldorin - duplicatas teriam sido apresentadas quando a questão foi formada, ele procura por "questões semelhantes", um recurso que as pessoas parecem não usar o suficiente.
Adam Houldsworth
@Adam: tente você mesmo. Copie / cole o título e veja quais duplicatas são apresentadas pelo sistema. Vou te dar uma dica: a resposta é nenhuma. Idem se você pesquisar no Google ou no SO. Parece que essa pergunta não foi feita antes.
Randolpho
Aaap ... Retiro o que disse. Acabei de encontrar uma quase duplicata, após algumas pesquisas muito dedicadas: stackoverflow.com/questions/2641692/… Agora, a pergunta é feita de forma totalmente diferente, mas a pergunta final é praticamente a mesma. Acho que podemos considerar isso um idiota, afinal.
Randolpho

Respostas:

167

Sim, Disposeserá chamado. É chamado assim que a execução sai do escopo do usingbloco, independentemente do meio que levou para sair do bloco, seja o fim da execução do bloco, uma returninstrução ou uma exceção.

Como @Noldorin corretamente aponta, o uso de um usingbloco no código é compilado em try/ finally, Disposesendo chamado no finallybloco. Por exemplo, o seguinte código:

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

efetivamente se torna:

MemoryStream ms = new MemoryStream();
try
{
    // code
    return 0;
}
finally
{
    ms.Dispose();
}

Portanto, como finallytem garantia de execução após o trybloco ter concluído a execução, independentemente de seu caminho de execução, Disposeé garantido que será chamado, não importa o quê.

Para obter mais informações, consulte este artigo do MSDN .

Adendo:
apenas uma pequena advertência a ser adicionada: como Disposeé garantido que será chamado, é quase sempre uma boa ideia garantir que Disposenunca lance uma exceção ao implementar IDisposable. Infelizmente, existem algumas classes na biblioteca central que fazer lance em determinadas circunstâncias, quando Disposeé chamado - Eu estou olhando para você, Proxy WCF Serviço de Referência / Cliente! - e quando isso acontece, pode ser muito difícil rastrear a exceção original se ela Disposefoi chamada durante o desenrolamento de uma pilha de exceções, uma vez que a exceção original é engolida em favor da nova exceção gerada pela Disposechamada. Pode ser enlouquecedoramente frustrante. Ou isso é frustrantemente enlouquecedor? Um dos dois. Talvez ambos.

Randolpho
fonte
4
Acho que você descobrirá que está efetivamente compilado em um bloco try-finally com uma chamada para Disposein finally, portanto, está efetivamente trabalhando na implementação de finally, conforme você descreve.
Noldorin
@Noldorin: exatamente. Embora eu possa ser explícito sobre isso. Edição em breve ....
Randolpho
1
Observe também que há algumas circunstâncias em que não há garantia de execução do bloco finally, como o uso de Environment.FailFast e se ocorrer uma StackOverFlowException.
Christopher McAtackney
@ C.McAtackney: também um bom ponto. Além disso, IIRC, OutOfMemoryException; basicamente, se você não puder capturar a exceção porque é uma falha de execução crítica, Dispose não será chamado. É claro que, nesse caso, o programa tem a garantia de travar, junto com qualquer memória alocada a ele, então em 99,9% dos casos não é um problema, a menos que você esteja fazendo coisas complicadas, como escrever em um arquivo em seu método de eliminação . Além do crash catastrófico do programa, claro.
Randolpho
Você nunca deve usar a instrução 'using ()' com o WCF - consulte este artigo para obter mais informações. Aqui está um snippet que uso para proxies WCF: 'WCFProxy variableName = null; tente {variableName = new WCFProxy (); // codifique TODO aqui variableName.Proxy.Close (); variableName.Dispose (); } catch (Exception) {if (variableName! = null && variableName.Proxy! = null) {variableName.Proxy.Abort (); } lançar; } '
Dave Black
18

usingas instruções se comportam exatamente como try ... finallyblocos, portanto, sempre serão executadas em quaisquer caminhos de saída de código. No entanto, acredito que eles estão sujeitos às raras e raras situações em que os finallyblocos não são chamados. Um exemplo que posso lembrar é se o encadeamento em primeiro plano sai enquanto os encadeamentos em segundo plano estão ativos: todos os encadeamentos exceto o GC são pausados, o que significa que os finallyblocos não são executados.

Edição óbvia: eles se comportam da mesma forma, independentemente da lógica que os permite manipular objetos ID descartáveis, d'oh.

Conteúdo bônus: eles podem ser empilhados (onde os tipos são diferentes):

using (SqlConnection conn = new SqlConnection("string"))
using (SqlCommand comm = new SqlCommand("", conn))
{

}

E também delimitado por vírgulas (onde os tipos são iguais):

using (SqlCommand comm = new SqlCommand("", conn), 
       SqlCommand comm2 = new SqlCommand("", conn))
{

}
Adam Houldsworth
fonte
4

Seu objeto MemoryStream será descartado corretamente, não há necessidade de se preocupar com isso.

Otávio Décio
fonte
0

Dê uma olhada em seu código no reflector depois de compilá-lo. Você descobrirá que o compilador refatora o código para garantir que dispose seja chamado no fluxo.

Wil P
fonte