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 .

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" ) );

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


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;

6

Essayez ceci:

         try
        {
            await AsyncFunction(...);
        }

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

(Voir la Wait()fin)


4

Utilisez C # 6.0. voir ce lien

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

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();
}

1

Vous pouvez utiliser une expression lambda comme suit:

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

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

        lambda(ex);
    }

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!)


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 
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.