Espera en bloque de captura

85

Tengo el siguiente 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" ) );
}

Básicamente, quiero descargar desde una URL y cuando falla con una excepción, quiero descargar desde otra URL. Ambos tiempos asincrónicos, por supuesto. Sin embargo, el código no se compila, debido a

error CS1985: No se puede esperar en el cuerpo de una cláusula de captura

Está bien, está prohibido por cualquier motivo, pero ¿cuál es el patrón de código correcto aquí?

EDITAR:

La buena noticia es que C # 6.0 probablemente permitirá llamadas en espera tanto en los bloques de captura como de finalmente .

György Balássy
fuente

Respuestas:

103

Actualización: C # 6.0 admite espera en la captura


Respuesta anterior : puede reescribir ese código para mover el awaitdesde el catchbloque usando una bandera:

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
fuente
7
Gracias svick, eso es bastante obvio, ¿algo mejor, más conectado a async?
György Balássy
No creo que exista nada de eso.
svick
3
En su caso, también podría utilizar la continuación de tareas. Pero la svickrespuesta del código in es más limpia que el código que usa continuaciones.
Stephen Cleary
16
Si incluso necesita volver a lanzar la excepción sin perder la pila de llamadas, también puede usar la System.Runtime.ExceptionServices.ExceptionDispatchInfoclase estática. Simplemente llame ExceptionDispatchInfo.Capture(ex)a su bloque de captura y almacene el valor de retorno, la excepción capturada, en una variable local. Una vez que haya terminado con su código asincrónico, puede usar el capturedException.Throw()que volverá a generar correctamente la excepción original.
Etienne Maheu
técnica impresionante
Zia Ur Rahman
9

Esto 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
fuente
6

Prueba esto:

         try
        {
            await AsyncFunction(...);
        }

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

(Ver el Wait()final)

Ron
fuente
4

Utilice C # 6.0. ver este enlace

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

El patrón que utilizo para volver a lanzar la excepción después de esperar en una tarea de respaldo:

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

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

Puede usar una expresión lambda de la siguiente manera:

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

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

        lambda(ex);
    }
Izmoto
fuente
Esto crea la lambda async void, que no debe usarse, a menos que sea necesario.
svick
0

Podrías poner el awaitdespués del bloque catch seguido de a label, y poner a gotoen el bloque try. (¡No, de verdad! ¡Los Goto no son tan malos!)

Protector uno
fuente
0

En un caso similar, no pude esperar en un bloque de captura. Sin embargo, pude establecer una bandera y usar la bandera en una declaración if (código a continuación)

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

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
fuente