Les méthodes qui renvoient une tâche jettent-elles des exceptions?

Les méthodes qui renvoient Task ont deux options pour signaler une erreur:

  1. jeter l’exception tout de suite
  2. renvoyer la tâche qui se terminera avec une exception

L’appelant doit-il s’attendre à ce que les deux types de rapports d’erreur soient signalés ou existe-t-il une norme / un accord limitant le comportement de la tâche à la deuxième option?

Exemple:

 class PageChecker { Task CheckWebPage(ssortingng url) { if(url == null) // Argument check throw Exception("Bad URL"); if(!HostPinger.IsHostOnline(url)) // Some other synchronous check throw Exception("Host is down"); return Task.Factory.StartNew(()=> { // Asynchronous check if(PageDownloader.GetPageContent(url).Contains("error")) throw Exception("Error on the page"); }); } } 

Manipuler les deux types est très moche:

 try { var task = pageChecker.CheckWebPage(url); task.ContinueWith(t => { if(t.Exception!=null) ReportBadPage(url); }); } catch(Exception ex) { ReportBadPage(url); } 

L’utilisation de async / wait peut aider, mais existe-t-il une solution pour .NET 4 sans support asynchrone?

La plupart des méthodes de restitution de Task sont destinées à être utilisées avec async / await (et ne doivent donc pas utiliser Task.Run ou Task.Factory.StartNew interne).

Notez qu’avec la méthode habituelle d’appel de méthodes asynchrones, la manière dont l’exception est levée n’a pas d’importance:

 await CheckWebPageAsync(); 

La différence n’intervient que lorsque la méthode est appelée puis attendue plus tard:

 List tasks = ...; tasks.Add(CheckWebPagesAsync()); ... await Task.WhenAll(tasks); 

Cependant, en général, l’appel ( CheckWebPagesAsync() ) et CheckWebPagesAsync() sont dans le même bloc de code. Ils sont donc dans le même bloc try / catch , et dans ce cas, cela n’a pas d’importance (généralement).

Existe-t-il une norme / accord qui limite le comportement de la tâche à la deuxième option?

Il n’y a pas de standard. Les préconditions sont un type d’ exception à tête de cloche , la manière dont elles sont lancées n’importe pas vraiment, car elles ne doivent jamais être capturées .

Jon Skeet est d’avis que les conditions préalables doivent être lancées directement (“en dehors” de la tâche renvoyée):

 Task CheckWebPageAsync(ssortingng url) { if(url == null) // argument check throw Exception("Bad url"); return CheckWebPageInternalAsync(url); } private async Task CheckWebPageInternalAsync(ssortingng url) { if((await PageDownloader.GetPageContentAsync(url)).Contains("error")) throw Exception("Error on the page"); } 

Cela fournit un bon parallèle avec les opérateurs LINQ, qui sont garantis de lancer des exceptions “tôt” comme ceci (en dehors de l’énumérateur).

Mais je ne pense pas que ce soit nécessaire. Je trouve que le code est plus simple lors de la définition des conditions préalables dans la tâche:

 async Task CheckWebPageAsync(ssortingng url) { if(url == null) // argument check throw Exception("Bad url"); if((await PageDownloader.GetPageContentAsync(url)).Contains("error")) throw Exception("Error on the page"); } 

Rappelez-vous qu’il ne devrait jamais y avoir de code prenant en compte les conditions préalables . Par conséquent, dans le monde réel, la manière dont l’exception est levée ne devrait faire aucune différence.

Par contre, c’est un point sur lequel je ne suis pas du tout d’accord avec Jon Skeet. Donc, votre kilométrage peut varier… beaucoup. 🙂