try / catch + using, sintaxe correta

189

Qual:

using (var myObject = new MyClass())
{
   try
   {
      // something here...
   }
   catch(Exception ex)
   {
      // Handle exception
   }
}

OU

try
{
   using (var myObject = new MyClass())
   {
      // something here...
   }
}
catch(Exception ex)
{
   // Handle exception
}
Xaqron
fonte
7
Apenas uma observação: deve-se ter o cuidado de capturar apenas exceções que realmente podem ser tratadas (corrigidas), exceto para registro ou quebra das mesmas.
John Saunders
1
Tenha em mente que também o último }da usingdeclaração pode lançar uma exceção como lembrado aqui .
Giulio Caccin
1
ATÉ que o depurador (no VS) não chame o método de descarte se você usar o primeiro bloco de código. Como a própria instrução using pode gerar uma exceção, ela me ajuda a usar o segundo bloco para garantir que o implícito finallychamado método de descarte.
ShooShoSha

Respostas:

98

Eu prefiro o segundo. Também pode interceptar erros relacionados à criação do objeto.

Jonathan Wood
fonte
11
Não concordo com este conselho. Se você espera que a criação do objeto gere um erro, qualquer manipulação dessa exceção deve ocorrer fora. Se houver alguma dúvida sobre para onde o manuseio deve ir, a exceção esperada deve ser outra - a menos que você esteja advogando a captura de qualquer exceção aleatória que possa ou não ser antecipada, que é um antipadrão clássico (fora de um manipulador de exceções não tratadas do processo ou do encadeamento).
Jeffrey L Whitledge
1
@ Jeffrey: A abordagem que eu descrevi me serviu bem e eu venho fazendo isso há muito tempo. Ninguém disse nada sobre a expectativa de falha na criação de objetos. Mas, ao agrupar uma operação que poderia falhar em potencial em um trybloco, que permite exibir uma mensagem de erro se algo falhar, o programa agora tem a capacidade de recuperar e informar o usuário.
Jonathan Wood
Sua resposta está correta, mas continua a sugestão de que a tentativa / captura deve estar lá (imediatamente) o tempo todo.
Henk Holterman
17
Acho que o primeiro também tem mérito, considere uma transação de banco de dados using( DBConnection conn = DBFactory.getConnection())que precisaria ser revertida em caso de uma exceção. Parece-me que ambos têm o seu lugar.
Wfoster
1
Isso também interceptará erros relacionados ao descarte do objeto.
Ahmad Ibrahim
39

Como um bloco using é apenas uma simplificação de sintaxe de uma tentativa / finalmente ( MSDN ), pessoalmente eu faria o seguinte, embora eu duvide que seja significativamente diferente da sua segunda opção:

MyClass myObject = null;
try {
  myObject = new MyClass();
  //important stuff
} catch (Exception ex) {
  //handle exception
} finally {
  if(myObject is IDisposable) myObject.Dispose();
}
chezy525
fonte
4
Por que você acha que adicionar um finallybloco é preferível à usingdeclaração?
Cody Gray
10
Adicionar um finallybloco que descarta um objeto IDisposable é o que uma usinginstrução faz. Pessoalmente, eu gosto disso em vez do usingbloco incorporado porque acho que indica mais claramente onde tudo está acontecendo e que tudo está no mesmo "nível". Eu também gosto disso mais do que vários usingblocos incorporados ... mas é tudo apenas a minha preferência.
precisa saber é o seguinte
8
Se você implementar muita manipulação de exceções, você realmente deve gostar de digitar! Essa palavra-chave "using" existe há algum tempo e seu significado é bastante claro para mim. E usá-lo ajuda a tornar o resto do meu código mais claro, mantendo a quantidade de desordem no mínimo.
Jonathan Wood
2
Isto está incorreto. O objeto deve ser instanciado fora da tryinstrução para ser descartado dentro da finallyinstrução; caso contrário, ele lançará um erro do compilador: "Uso da variável local não atribuída 'myObject'" "
Steve Konves 02/07/12
3
Tecnicamente, isso também não será compilado. Cannot assign null to implicitly-typed local variable;) Mas eu sei o que você quer dizer e, pessoalmente, prefere isso a aninhar um bloco usando.
quer
20

Depende. Se você estiver usando o Windows Communication Foundation (WCF), using(...) { try... }não funcionará corretamente se o proxy na usinginstrução estiver no estado de exceção, ou seja, a eliminação deste proxy causará outra exceção.

Pessoalmente, acredito na abordagem de manipulação mínima, ou seja, lide apenas com a exceção que você conhece no ponto de execução. Em outras palavras, se você souber que a inicialização de uma variável usingpode gerar uma exceção específica, eu a envolvo try-catch. Da mesma forma, se usingalgo dentro do corpo pode acontecer, que não está diretamente relacionado à variável using, então envolvo-o com outro trypara essa exceção específica. Eu raramente uso Exceptionnas minhas catches.

Mas eu gosto IDisposablee, usingportanto, talvez seja tendenciosa.

Schultz9999
fonte
19

Se sua instrução catch precisar acessar a variável declarada em uma instrução using, então inside é sua única opção.

Se a sua instrução catch precisar do objeto referenciado no uso antes de ser descartado, o interior é sua única opção.

Se a sua declaração de captura executar uma ação de duração desconhecida, como exibir uma mensagem para o usuário, e você desejar descartar seus recursos antes que isso aconteça, a parte externa é sua melhor opção.

Sempre que eu tenho um cenário semelhante a esse, o bloco try-catch geralmente está em um método diferente, além da pilha de chamadas do uso. Não é típico para um método saber como lidar com exceções que ocorrem dentro dele dessa maneira.

Portanto, minha recomendação geral é do lado de fora - do lado de fora.

private void saveButton_Click(object sender, EventArgs args)
{
    try
    {
        SaveFile(myFile); // The using statement will appear somewhere in here.
    }
    catch (IOException ex)
    {
        MessageBox.Show(ex.Message);
    }
}
Jeffrey L Whitledge
fonte
10

Ambos são sintaxe válida. Na verdade, tudo se resume ao que você deseja fazer: se você deseja capturar erros relacionados à criação / descarte do objeto, use o segundo. Caso contrário, use o primeiro.

Smashery
fonte
8

Há uma coisa importante que chamarei aqui: a primeira não capturará nenhuma exceção resultante de chamar o MyClassconstrutor.

Madhur Ahuja
fonte
3

No C # 8.0, eu prefiro usar o segundo igual a este

public class Person : IDisposable
{
    public Person()
    {
        int a = 0;
        int b = Id / a;
    }
    public int Id { get; set; }

    public void Dispose()
    {
    }
}

e depois

static void Main(string[] args)
    {

        try
        {
            using var person = new Person();
        }
        catch (Exception ex) when
        (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
        ex.TargetSite.MemberType == System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Constructor Person");
        }
        catch (Exception ex) when
       (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
       ex.TargetSite.MemberType != System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Person");
        }
        catch (Exception ex)
        {
            Debug.Write(ex.Message);
        }
        finally
        {
            Debug.Write("finally");
        }
    }
Reza Jenabi
fonte
1

Se o objeto que você está inicializando no bloco Using () pode lançar qualquer exceção, você deve procurar a segunda sintaxe, caso contrário ambas são igualmente válidas.

No meu cenário, eu tive que abrir um arquivo e estava passando filePath no construtor do objeto que estava inicializando no bloco Using () e isso pode gerar uma exceção se o filePath estiver errado / vazio. Portanto, neste caso, a segunda sintaxe faz sentido.

Meu código de amostra: -

try
{
    using (var obj= new MyClass("fileName.extension"))
    {

    }
}
catch(Exception ex)
{
     //Take actions according to the exception.
}
Ankur Arora
fonte
1

A partir do C # 8.0 , você pode simplificar as usinginstruções sob algumas condições para se livrar do bloco aninhado e, em seguida, ele se aplica apenas ao bloco anexo.

Portanto, seus dois exemplos podem ser reduzidos a:

using var myObject = new MyClass();
try
{
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

E:

try
{
   using var myObject = new MyClass();
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

Ambos são bem claros; e isso reduz a escolha entre os dois a uma questão de como você deseja que o escopo do objeto esteja, onde você deseja lidar com erros de instanciação e quando deseja descartá-lo.

Jason C
fonte