Aguarde no bloco de captura

85

Eu tenho o seguinte código:

WebClient wc = new WebClient();
string result;
try
{
  result = await wc.DownloadStringTaskAsync( new Uri( "http://badurl" ) );
}
catch
{
  result = await wc.DownloadStringTaskAsync( new Uri( "http://fallbackurl" ) );
}

Basicamente, quero fazer o download de um URL e, quando falha com uma exceção, quero fazer o download de outro URL. Ambos são assíncronos, é claro. No entanto, o código não compila, por causa de

erro CS1985: não é possível esperar no corpo de uma cláusula catch

OK, é proibido por qualquer motivo, mas qual é o padrão de código correto aqui?

EDITAR:

A boa notícia é que o C # 6.0 provavelmente permitirá chamadas de espera em blocos catch e finally .

György Balássy
fonte

Respostas:

103

Atualização: C # 6.0 oferece suporte a await in catch


Resposta antiga : você pode reescrever esse código para mover o awaitdo catchbloco usando um sinalizador:

WebClient wc = new WebClient();
string result = null;
bool downloadSucceeded;
try
{
  result = await wc.DownloadStringTaskAsync( new Uri( "http://badurl" ) );
  downloadSucceeded = true;
}
catch
{
  downloadSucceeded = false;
}

if (!downloadSucceeded)
  result = await wc.DownloadStringTaskAsync( new Uri( "http://fallbackurl" ) );
Svick
fonte
7
Obrigado svick, isso é bastante óbvio, algo melhor, mais conectado ao assíncrono?
György Balássy de
Eu não acho que algo assim exista.
svick de
3
No seu caso, você também pode usar continuações de tarefas. Mas o código na svickresposta de é mais limpo do que o código que usa continuações.
Stephen Cleary
16
Se você precisar relançar a exceção sem perder a pilha de chamadas, também poderá usar a System.Runtime.ExceptionServices.ExceptionDispatchInfoclasse estática. Simplesmente chame ExceptionDispatchInfo.Capture(ex)seu bloco catch e armazene o valor de retorno, a exceção capturada, em uma variável local. Depois de concluir o código assíncrono, você pode usá-lo capturedException.Throw()para relançar corretamente a exceção original.
Etienne Maheu
técnica incrível
Zia Ur Rahman
24

Aguardar em um bloco de captura agora é possível a partir da Visualização do usuário final de Roslyn, conforme mostrado aqui (listado em Aguardar em captura / finalmente) e será incluído no C # 6.

O exemplo listado é

try … catch { await … } finally { await … }

Update: Adicionado link mais recente, e que estará em C # 6

Craig
fonte
9

Isso parece funcionar.

        WebClient wc = new WebClient();
        string result;
        Task<string> downloadTask = wc.DownloadStringTaskAsync(new Uri("http://badurl"));
        downloadTask = downloadTask.ContinueWith(
            t => {
                return wc.DownloadStringTaskAsync(new Uri("http://google.com/")).Result;
            }, TaskContinuationOptions.OnlyOnFaulted);
        result = await downloadTask;
Darragh
fonte
6

Experimente:

         try
        {
            await AsyncFunction(...);
        }

        catch(Exception ex)
        { 
            Utilities.LogExceptionToFile(ex).Wait();
            //instead of "await Utilities.LogExceptionToFile(ex);"
        }

(Veja o Wait()final)

Ron
fonte
4

Use C # 6.0. veja este link

public async Task SubmitDataToServer()
{
  try
  {
    // Submit Data
  }
  catch
  {
    await LogExceptionAsync();
  }
  finally
  {
    await CloseConnectionAsync();
  }
}
Mafii
fonte
1

O padrão que uso para relançar a exceção após aguardar em uma tarefa de fallback:

ExceptionDispatchInfo capturedException = null;
try
{
  await SomeWork();
}
catch (Exception e)
{
  capturedException = ExceptionDispatchInfo.Capture(e);
}

if (capturedException != null)
{
  await FallbackWork();
  capturedException.Throw();
}
Hansmaad
fonte
1

Você pode usar uma expressão lambda da seguinte maneira:

  try
    {
        //.....
    }
    catch (Exception ex)
    {
        Action<Exception> lambda;

        lambda = async (x) =>
        {
            // await (...);
        };

        lambda(ex);
    }
Izmoto
fonte
Isso cria o lambda async void, que não deve ser usado, a menos que seja necessário.
svick
0

Você poderia colocar o awaitapós o bloco catch seguido por a label, e colocar a gotono bloco try. (Não, sério! Goto's não são tão ruins!)

Protetor um
fonte
0

Em uma instância semelhante, não pude esperar em um bloco de captura. No entanto, consegui definir um sinalizador e usar o sinalizador em uma instrução if (código abaixo)

---------------------------------------...

boolean exceptionFlag = false; 

try 
{ 
do your thing 
} 
catch 
{ 
exceptionFlag = true; 
} 

if(exceptionFlag == true){ 
do what you wanted to do in the catch block 
}
Amanda Berenice
fonte