existe-t-il une "meilleure pratique" MS ou un contrat de contrat lors de la mise en œuvre d'une méthode qui renvoie une tâche vis-à-vis des exceptions? Ceci est arrivé lors de la rédaction des tests d'unité et j'essayais de comprendre si je devais tester / gérer cette condition (je reconnais que la réponse pourrait être "codage défensive", mais je ne veux pas que ce soit la réponse). Ie p>
La méthode doit toujours renvoyer une tâche, qui devrait contenir l'exception lancée. P> li>
Méthode doit toujours renvoyer une tâche, sauf lorsque la méthode fournit des arguments non valides (c'est-à-dire argumentException). P> li>
La méthode doit toujours renvoyer une tâche, sauf lorsque le développeur va voyou et fait ce qu'il veut (JK). P> Li> OL>
Task Foo1Async(string id){ if(id == null){ throw new ArgumentNullException(); } // do stuff } Task Foo2Async(string id){ if(id == null){ var source = new TaskCompletionSource<bool>(); source.SetException(new ArgumentNullException()); return source.Task; } // do stuff } Task Bar(string id){ // argument checking if(id == null) throw new ArgumentNullException("id") try{ return this.SomeService.GetAsync(id).ContinueWith(t => { // checking for Fault state here // pass exception through. }) }catch(Exception ex){ // handling more Fault state here. // defensive code. // return Task with Exception. var source = new TaskCompletionSource<bool>(); source.SetException(ex); return source.Task; } }
3 Réponses :
Le cas général lorsque les méthodes de retour des méthodes sont parce qu'elles sont des méthodes asynchrones. Dans ces cas, il est courant qu'une exception dans la partie synchrone de la méthode doit être lancée, tout comme dans une autre méthode, et dans la partie asynchrone doit être stockée dans la tâche code> renvoyée code> (automatiquement en appelant un < code> async code> méthode ou délégué anonyme). p>
Donc, dans les cas simples, tels que des paramètres non valides, lancez simplement une exception comme dans Cette réponse suppose que vous parlez de FOO1ASYNC code>. Dans l'affaire plus complexe concernant l'opération asynchrone définit une exception sur la tâche renvoyée comme dans
FOO2ASYNC CODE> P>
Task Code> Les méthodes de retour qui ne sont pas marquées avec
async code>. Dans ceux qui ne sont pas contrôlés sur la tâche créée et que toute exception serait automatiquement stockée dans cette tâche (la question serait donc non pertinente). P>
+1. D'accord. Des trucs comme la validation des paramètres n'ont aucune raison d'attendre. Si vous ne pouvez pas jeter immédiatement, mettez-le dans la tâche.
J'ai mis à jour l'exemple avec la barre (chaîne): tâche. La barre a une dépendance sur certains services qui renvoie une tâche. L'exemple doit exception / état de défaut est traité dans 2 zones (essayez de bloquer et de continuer.).
@Kevin oui. C'est à peu près ce que je voulais dire
@ l3arnon: Dans ces cas, il est courant qu'une exception dans la partie synchrone de la méthode doit être lancée comme dans une autre méthode, et dans la partie asynchrone doit être stockée à l'intérieur de la tâche renvoyée. i> peu importe Si vous lancez de la partie synchrone ou asynchrone de async code> méthode. Dans les deux cas, l'exception sera stockée dans la tâche
code>. La seule différence est que l'objet
Task code> sera terminé instantanément (défaut) dans le premier cas. Si
async code> la méthode jette de la partie synchrone, mais l'appelant ne doit pas
attendre code> la tâche renvoyée, l'exception ne sera pas retirée.
@Noseratio Les méthodes de l'exemple sont asynchrones, mais non marquées avec async code> qui est assez courante lorsque vous supprimez le modificateur
async code> async code> où il est inutile.
@Kevin et voici les pensées de Jon Skeet à ce sujet: "Nous sommes dans une situation similaire ici, si nous voulons que des arguments soient validés avec impatience, ce qui a provoqué une exception directement à l'appelant" Comment pouvons-nous valider les arguments?
@ L3arnon, mon point est que l'appelant ne doit pas faire de supposer si la méthode a async code> signature. Je ne pense pas que ce soit une bonne idée d'utiliser
TaskCompletSource code> pour propager des exceptions comme @kevin fait à l'intérieur de la barre
code>, je ferais simplement
renvoyer cela.someservice.getaSync (id) code>.
@Noseratio Il ne s'agit pas de l'appelant, il s'agit de savoir comment mettre en œuvre la méthode elle-même. À propos de bar code>, retourner
someervice.getaSync (id) code> est exactement ce que j'ai dit. L'exception de synchronisation est lancée et l'exception ASYNC est dans la tâche
code>
J'ai récemment posé une question quelque peu similaire:
Gestion des exceptions de la partie synchrone de la méthode async . P>
Si la méthode comporte un si la méthode n'a pas imo, dans les deux cas, l'appelant ne doit pas faire d'hypothèse forte> sur la question de savoir si l'exception a été lancée à partir de la synchrone ou de l'exception. partie asynchrone, ou si la méthode a Si vous avez vraiment besoin de savoir si la tâche est terminée de manière synchrone, vous pouvez toujours vérifier sa tâche async code> Signature, peu importe si vous jetez du synchrones ou partie asynchrone de la méthode. Dans les deux cas, l'exception sera stockée à l'intérieur de la tâche code> code>. La seule différence est que la tâche
résultante code> sera terminée instantanément (défectueuse) dans l'ancien cas. P>
async code> Signature, l'exception peut être projetée sur le cadre de pile de l'appelant. P>
async code> signature, du tout. p>
.Commété code> /
défaut code> /
annulé code> Statut ou
Taste.Exception code> propriété, sans attente: p>
Task Foo1Async(string id){
if(id == null) {
throw new ArgumentNullException();
}
// do stuff
}
Task Foo2Async(string id) {
if(id == null){
throw new ArgumentNullException();
}
// do stuff
}
Task Bar(string id) {
// argument checking
if(id == null) throw new ArgumentNullException("id")
return this.SomeService.GetAsync(id);
}
Je sais que Jon Skeet est un fan de vérification de style de préconformation dans une méthode synchrone distincte de sorte qu'elles soient jetées directement. P>
Cependant, ma propre opinion est "ça n'a pas d'importance". Considérez Eric Lippert's taxonomie d'exception . Nous convenons tous que les exceptions exogènes doivent être placées sur la tâche code> renvoyée code> (non jetée directement sur le cadre de la pile de l'appelant). Les exceptions vexées doivent être complètement évitées. Les seuls types d'exceptions en question sont des exceptions osseuses (par exemple, des exceptions d'arguments). P>
Mon argument est que peu importe comment ils sont jetés parce que vous ne devriez pas écrire de code de production qui les attrape em>. Les tests de votre unité sont le seul code qui devrait attraper argumentexception code> et amis, et si vous utilisez
attendre code> alors cela n'a pas d'importance quand ils sont lancés. P>
Dans le cas de l'OP, l'exception éventuelle éventuelle possible de la barre par code> par
this.someservice.getaSync () code> est considéré comme exogène (je le pense)? Devrait-il être ensuite enveloppé avec
tcs.setexception code>, la façon dont il est fait là-bas?
Oui, c'est exogène. Je voudrais implémenter bar code> avec
async code> /
attendre code>, qui utiliserait
tcs.setexception code> sous les couvercles.
J'apprécierais que si vous pouviez montrer comment bar code> regarderait avec i>
taskcompletsource code> et
async / attendre code>. J'ai envie de me manquer quelque chose d'évident. Merci!
L'approche TCS code> est ce que l'OP a écrit dans sa question. Le
async code> /
attendre code> serait
si (id == null) lancer une nouvelle argumentNullexception ("ID"); Attendre cela.someservice.getaSync (ID); code> (Ignoring the "Retourner simplement l'optimisation de la tâche
getasync code> tâche").
Ah, donc c'est i> tcs ou async / attendre code> comme ce Pastebin. com / ncxh3t9x ?
@Noeratio: Oui, c'est ce que je voulais dire.
Question interessante. Je n'ai jamais vu une telle directive, alors je serais tenté de dire 4: la méthode peut adresser une exception ou renvoyer une tâche contenant cette exception, et l'appelant doit traiter ces cas équivalents.
Le codage défensif @HVD suggérerait que l'appelant manipule pour les deux cas, mais le minimaliste en moi ne veut pas gérer des exceptions exprimées de plusieurs manières. L'exception serait une vérification des arguments (en train de me contredire).
Dans le même temps, cependant, la manière dont une exception est lancée ne devrait pas dépendre de la mise en œuvre de la méthode à l'aide de
async code>, car c'est un détail de mise en œuvre non visible à l'appelant. Si vous êtes d'accord avec cela, alors les deux cas doivent être traités comme équivalents, sinon vous ne devriez jamais résoudre de manière synchrone.