Attendre dans le bloc de capture

85

J'ai le code suivant:

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

Fondamentalement, je veux télécharger à partir d'une URL et en cas d'échec avec une exception, je veux télécharger à partir d'une autre URL. Les deux temps asynchrone bien sûr. Cependant, le code ne se compile pas, à cause de

erreur CS1985: impossible d'attendre dans le corps d'une clause catch

OK, c'est interdit pour une raison quelconque, mais quel est le modèle de code correct ici?

ÉDITER:

La bonne nouvelle est que C # 6.0 autorisera probablement les appels d'attente à la fois dans les blocs catch et finally .

György Balássy
la source

Réponses:

103

Mise à jour: C # 6.0 prend en charge wait in catch


Ancienne réponse : vous pouvez réécrire ce code pour déplacer le awaitdu catchbloc en utilisant un indicateur:

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
la source
7
Merci svick, c'est assez évident, rien de mieux, plus connecté à async?
György Balássy
Je ne pense pas que quelque chose comme ça existe.
svick
3
Dans votre cas, vous pouvez également utiliser des continuations de tâches. Mais le code dans svickla réponse est plus propre que le code utilisant des continuations.
Stephen Cleary
16
Si vous avez même besoin de renvoyer l'exception sans perdre la pile d'appels, vous pouvez également utiliser la System.Runtime.ExceptionServices.ExceptionDispatchInfoclasse statique. Appelez simplement ExceptionDispatchInfo.Capture(ex)votre bloc catch et stockez la valeur de retour, l'exception capturée, dans une variable locale. Une fois que vous avez terminé avec votre code asynchrone, vous pouvez utiliser capturedException.Throw()ce qui renverra correctement l'exception d'origine.
Etienne Maheu
awesome technique
Zia Ur Rahman
24

Attendre dans un bloc catch est désormais possible à partir de l'aperçu utilisateur final de Roslyn comme indiqué ici (répertorié sous Attendre dans catch / finally) et sera inclus dans C # 6.

L'exemple listé est

trycatch { await … } finally { await … }

Mise à jour: Ajout d'un lien plus récent et qu'il sera en C # 6

Craig
la source
9

Cela semble fonctionner.

        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
la source
6

Essayez ceci:

         try
        {
            await AsyncFunction(...);
        }

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

(Voir la Wait()fin)

Ron
la source
4

Utilisez C # 6.0. voir ce lien

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

Le modèle que j'utilise pour renvoyer l'exception après avoir attendu sur une tâche de secours:

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

if (capturedException != null)
{
  await FallbackWork();
  capturedException.Throw();
}
hansmaad
la source
1

Vous pouvez utiliser une expression lambda comme suit:

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

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

        lambda(ex);
    }
Izmoto
la source
Cela rend le lambda async void, qui ne devrait pas être utilisé, sauf si vous devez le faire.
svick
0

Vous pouvez mettre le awaitaprès le bloc catch suivi d'un label, et mettre un gotodans le bloc try. (Non, vraiment! Les goûts ne sont pas si mauvais!)

Protecteur un
la source
0

Dans un cas similaire, je n'ai pas pu attendre dans un bloc catch. Cependant, j'ai pu définir un drapeau et utiliser le drapeau dans une instruction if (code ci-dessous)

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

boolean exceptionFlag = false; 

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

if(exceptionFlag == true){ 
do what you wanted to do in the catch block 
}
Amanda Bérénice
la source