Oczekuj w bloku catch

85

Mam następujący kod:

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

Zasadniczo chcę pobierać z adresu URL, a jeśli się nie powiedzie, z wyjątkiem, chcę pobrać z innego adresu URL. Oczywiście oba czasy są asynchroniczne. Jednak kod nie kompiluje się z powodu

błąd CS1985: nie można czekać w treści klauzuli catch

OK, z jakiegoś powodu jest to zabronione, ale jaki jest prawidłowy wzorzec kodu?

EDYTOWAĆ:

Dobrą wiadomością jest to, że C # 6.0 prawdopodobnie zezwoli na wywołania await zarówno w blokach catch, jak i na końcu .

György Balássy
źródło

Odpowiedzi:

103

Aktualizacja: obsługa języka C # 6.0 czeka w catch


Stara odpowiedź : Możesz przepisać ten kod, aby przenieść awaitz catchbloku za pomocą flagi:

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
źródło
7
Dzięki Svick, to całkiem oczywiste, coś lepszego, bardziej połączonego z asynchronicznym?
György Balássy
Nie sądzę, żeby coś takiego istniało.
Svick
3
W Twoim przypadku możesz również użyć kontynuacji zadań. Ale kod w svickodpowiedzi jest czystszy niż kod używający kontynuacji.
Stephen Cleary,
16
Jeśli nawet chcesz ponownie zgłosić wyjątek bez utraty stosu wywołań, możesz również użyć System.Runtime.ExceptionServices.ExceptionDispatchInfoklasy statycznej. Po prostu wywołaj ExceptionDispatchInfo.Capture(ex)swój blok catch i zapisz zwracaną wartość, przechwycony wyjątek, w zmiennej lokalnej. Gdy skończysz z kodem asynchronicznym, możesz użyć, capturedException.Throw()który poprawnie wyrzuci oryginalny wyjątek.
Etienne Maheu
niesamowita technika
Zia Ur Rahman
9

To wydaje się działać.

        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
źródło
6

Spróbuj:

         try
        {
            await AsyncFunction(...);
        }

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

(Zobacz Wait()zakończenie)

Ron
źródło
4

Użyj C # 6.0. zobacz ten link

public async Task SubmitDataToServer()
{
  try
  {
    // Submit Data
  }
  catch
  {
    await LogExceptionAsync();
  }
  finally
  {
    await CloseConnectionAsync();
  }
}
Mafii
źródło
1

Wzorzec, którego używam, aby ponownie zgłosić wyjątek po oczekiwaniu na zadanie rezerwowe:

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

if (capturedException != null)
{
  await FallbackWork();
  capturedException.Throw();
}
hansmaad
źródło
1

Możesz użyć wyrażenia lambda w następujący sposób:

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

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

        lambda(ex);
    }
Izmoto
źródło
To sprawia, że ​​lambda async void, które nie powinny być używane, chyba że musisz.
svick
0

Możesz umieścić awaitpo bloku catch, po którym następuje a label, i wstawić a gotow bloku try. (Nie, naprawdę! Gotowe nie są takie złe!)

Obrońca pierwszy
źródło
0

W podobnym przypadku nie mogłem czekać w bloku catch. Jednak udało mi się ustawić flagę i użyć flagi w instrukcji if (kod poniżej)

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

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
źródło