Warten Sie im Fangblock

85

Ich habe folgenden Code:

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

Grundsätzlich möchte ich von einer URL herunterladen und wenn es mit einer Ausnahme fehlschlägt, möchte ich von einer anderen URL herunterladen. Beide Male natürlich asynchron. Der Code wird jedoch aufgrund von nicht kompiliert

Fehler CS1985: Kann im Hauptteil einer catch-Klausel nicht warten

OK, es ist aus irgendeinem Grund verboten, aber wie lautet das richtige Codemuster hier?

BEARBEITEN:

Die gute Nachricht ist, dass C # 6.0 wahrscheinlich das Warten auf Anrufe sowohl im Catch- als auch im Endblock ermöglicht .

György Balássy
quelle

Antworten:

103

Update: C # 6.0 unterstützt das Warten im Fang


Alte Antwort : Sie können diesen Code neu schreiben, um ihn mit einem Flag awaitaus dem catchBlock zu verschieben :

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
quelle
7
Danke svick, das ist ganz offensichtlich, etwas Besseres, mehr mit Async verbunden?
György Balássy
Ich glaube nicht, dass so etwas existiert.
Svick
3
In Ihrem Fall können Sie auch Aufgabenfortsetzungen verwenden. Der Code in svickder Antwort ist jedoch sauberer als der Code mit Fortsetzungen.
Stephen Cleary
16
Wenn Sie die Ausnahme erneut auslösen müssen, ohne den Callstack zu verlieren, können Sie auch die System.Runtime.ExceptionServices.ExceptionDispatchInfostatische Klasse verwenden. Rufen ExceptionDispatchInfo.Capture(ex)Sie einfach Ihren catch-Block auf und speichern Sie den Rückgabewert, die erfasste Ausnahme, in einer lokalen Variablen. Sobald Sie mit Ihrem asynchronen Code fertig sind, können Sie verwenden, capturedException.Throw()wodurch die ursprüngliche Ausnahme ordnungsgemäß erneut ausgelöst wird.
Etienne Maheu
tolle Technik
Zia Ur Rahman
24

Das Warten in einem Catch-Block ist jetzt ab der hier gezeigten Endbenutzer-Vorschau von Roslyn möglich (aufgeführt unter Await in catch / finally) und wird in C # 6 aufgenommen.

Das aufgeführte Beispiel ist

try  catch { await  } finally { await  }

Update: Neuerer Link hinzugefügt, und dass es in C # 6 sein wird

Craig
quelle
9

Das scheint zu funktionieren.

        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
quelle
6

Probieren Sie es aus:

         try
        {
            await AsyncFunction(...);
        }

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

(Siehe das Wait()Ende)

Ron
quelle
4

Verwenden Sie C # 6.0. siehe diesen Link

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

Das Muster, mit dem ich die Ausnahme nach dem Warten auf eine Fallback-Aufgabe erneut auslöse:

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

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

Sie können einen Lambda-Ausdruck wie folgt verwenden:

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

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

        lambda(ex);
    }
Izmoto
quelle
Dies macht das Lambda async void, das nicht verwendet werden sollte, es sei denn, Sie müssen.
Svick
0

Sie können das awaitnach dem catch-Block gefolgt von a labelsetzen und ein gotoin den try-Block setzen. (Nein, wirklich! Goto ist nicht so schlimm!)

Beschützer eins
quelle
0

In einem ähnlichen Fall konnte ich nicht in einem Fangblock warten. Ich konnte jedoch ein Flag setzen und das Flag in einer if-Anweisung verwenden (Code unten).

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

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
quelle