Encontrei esse novo recurso em C # que permite que um manipulador catch seja executado quando uma condição específica for atendida.
int i = 0;
try
{
throw new ArgumentNullException(nameof(i));
}
catch (ArgumentNullException e)
when (i == 1)
{
Console.WriteLine("Caught Argument Null Exception");
}
Estou tentando entender quando isso pode ser útil.
Um cenário poderia ser mais ou menos assim:
try
{
DatabaseUpdate()
}
catch (SQLException e)
when (driver == "MySQL")
{
//MySQL specific error handling and wrapping up the exception
}
catch (SQLException e)
when (driver == "Oracle")
{
//Oracle specific error handling and wrapping up of exception
}
..
mas isso é algo que posso fazer no mesmo manipulador e delegar a métodos diferentes dependendo do tipo de driver. Isso torna o código mais fácil de entender? Provavelmente não.
Outro cenário que posso imaginar é algo como:
try
{
SomeOperation();
}
catch(SomeException e)
when (Condition == true)
{
//some specific error handling that this layer can handle
}
catch (Exception e) //catchall
{
throw;
}
Novamente, isso é algo que posso fazer como:
try
{
SomeOperation();
}
catch(SomeException e)
{
if (condition == true)
{
//some specific error handling that this layer can handle
}
else
throw;
}
Usar o recurso 'catch, when' torna o tratamento de exceções mais rápido porque o manipulador é ignorado e o desenrolamento da pilha pode acontecer muito mais cedo quando comparado ao tratamento de casos de uso específicos dentro do manipulador? Existem casos de uso específicos que se adaptam melhor a esse recurso que as pessoas podem adotar como uma boa prática?
fonte
when
necessidade de acessar a própria exceçãotry..catch...catch..catch..finally
?catch (Exception ex)
, verificar o tipo e dethrow
outra forma. Um código um pouco mais organizado (também conhecido como evitar o ruído do código) é exatamente por que esse recurso existe. (Isso é verdade para vários recursos.)Respostas:
Os blocos de captura já permitem que você filtre o tipo de exceção:
A
when
cláusula permite que você estenda este filtro para expressões genéricas.Portanto, você usa a
when
cláusula para casos em que o tipo de exceção não é distinto o suficiente para determinar se a exceção deve ser tratada aqui ou não.Um caso de uso comum são os tipos de exceção que, na verdade, são um invólucro para vários tipos de erros diferentes.
Aqui está um caso que eu realmente usei (em VB, que já tem esse recurso há algum tempo):
Mesmo para
SqlException
, que também possui umErrorCode
imóvel. A alternativa seria algo assim:que é indiscutivelmente menos elegante e quebra ligeiramente o rastreamento de pilha .
Além disso, você pode mencionar o mesmo tipo de exceção duas vezes no mesmo bloco try-catch:
o que não seria possível sem a
when
condição.fonte
catch
, não é?when
permite que você trate o mesmo tipo de exceção várias vezes. Você também deve mencionar isso, pois é uma diferença crucial. Semwhen
você obterá um erro do compilador.Do wiki de Roslyn (ênfase minha):
O primeiro ponto vale a pena demonstrar.
Se executarmos isso no WinDbg até que a exceção seja atingida, e imprimirmos a pilha usando
!clrstack -i -a
, veremos apenas o quadro deA
:No entanto, se mudarmos o programa para usar
when
:Veremos que a pilha também contém
B
o quadro de:Essas informações podem ser muito úteis ao depurar despejos de memória.
fonte
throw;
(ao invés dethrow ex;
) deixar a pilha ilesa também? 1 para o efeito colateral. Não tenho certeza se aprovo isso, mas é bom saber sobre essa técnica.throw;
, a pilha se desenrola e você perde os valores dos parâmetros.throw;
muda um pouco o rastreamento de pilha ethrow ex;
muito.throw
perturba um pouco o rastreamento da pilha. Os números das linhas são diferentes quando usadosthrow
em vez dewhen
.Quando uma exceção é lançada, a primeira passagem de tratamento de exceção identifica onde a exceção será capturada antes de desenrolar a pilha; se / quando o local "catch" é identificado, todos os blocos "finally" são executados (observe que se uma exceção escapar de um bloco "finally", o processamento da exceção anterior pode ser abandonado). Assim que isso acontecer, o código irá retomar a execução no "catch".
Se houver um ponto de interrupção dentro de uma função que é avaliada como parte de um "quando", esse ponto de interrupção suspenderá a execução antes que ocorra qualquer reversão da pilha; por outro lado, um ponto de interrupção em um "catch" só suspenderá a execução depois que todos os
finally
manipuladores forem executados.Finalmente, se as linhas 23 e 27 da
foo
chamadabar
, e a chamada na linha 23 lançar uma exceção que é capturadafoo
e relançada na linha 57, o rastreamento de pilha irá sugerir que a exceção ocorreu durante a chamadabar
da linha 57 [localização do relançamento] , destruindo qualquer informação sobre se a exceção ocorreu na chamada da linha 23 ou da linha 27. Usarwhen
para evitar capturar uma exceção em primeiro lugar evita tal perturbação.BTW, um padrão útil que é irritantemente estranho em C # e VB.NET é usar uma chamada de função dentro de uma
when
cláusula para definir uma variável que pode ser usada dentro de umafinally
cláusula para determinar se a função foi concluída normalmente, para lidar com casos em que uma função não tem esperança de "resolver" qualquer exceção que ocorra, mas deve, no entanto, agir com base nela. Por exemplo, se uma exceção é lançada dentro de um método de fábrica que deve retornar um objeto que encapsula recursos, quaisquer recursos adquiridos precisarão ser liberados, mas a exceção subjacente deve chegar até o chamador. A maneira mais limpa de lidar com isso semanticamente (embora não sintaticamente) é ter umfinally
bloco verificar se ocorreu uma exceção e, em caso afirmativo, libera todos os recursos adquiridos em nome do objeto que não será mais retornado. Como o código de limpeza não tem esperança de resolver qualquer condição que tenha causado a exceção, ele realmente não deveriacatch
, mas apenas precisa saber o que aconteceu. Chamando uma função como:dentro de um
when
cláusula permitirá que a função de fábrica saiba que algo aconteceu.fonte