J'ai récemment commencé un travail de programmation en C #, mais j'ai pas mal de connaissances en Haskell.
Mais je comprends que C # est un langage orienté objet, je ne veux pas forcer une cheville ronde dans un trou carré.
J'ai lu l'article Exception Throwing de Microsoft qui dit:
NE PAS renvoyer les codes d'erreur.
Mais étant habitué à Haskell, j'utilise le type de données C # OneOf
, renvoyant le résultat sous forme de valeur "droite" ou l'erreur (le plus souvent une énumération) sous forme de valeur "à gauche".
Cela ressemble beaucoup à la convention de Either
Haskell.
Pour moi, cela semble plus sûr que les exceptions. En C #, ignorer les exceptions ne génère pas d'erreur de compilation et si elles ne sont pas interceptées, elles ne font que bouillonner et faire planter votre programme. C'est peut-être mieux que d'ignorer un code d'erreur et de produire un comportement indéfini, mais bloquer le logiciel de votre client n'est toujours pas une bonne chose, en particulier lorsqu'il effectue de nombreuses autres tâches importantes en arrière-plan.
Avec OneOf
, il faut être assez explicite sur le déballage et le traitement de la valeur de retour et des codes d'erreur. Et si on ne sait pas comment le gérer à ce stade de la pile d'appels, il doit être placé dans la valeur de retour de la fonction actuelle, afin que les appelants sachent qu'une erreur peut en résulter.
Mais cela ne semble pas être l'approche suggérée par Microsoft.
L’utilisation OneOf
d’exceptions au lieu d’exceptions pour la gestion des exceptions "ordinaires" (comme File Not Found, etc.) est-elle une approche raisonnable ou est-ce une pratique épouvantable?
Il est à noter que j'ai entendu dire que les exceptions en tant que flux de contrôle sont considérées comme un anti-modèle sérieux , donc si "l'exception" est quelque chose que vous géreriez normalement sans mettre fin au programme, n'est-ce pas un "flux de contrôle"? Je comprends qu'il y a une zone d'ombre.
Remarque Je n'utilise pas OneOf
pour des choses comme "Out Of Memory", des conditions dont je ne m'attends pas à récupérer vont toujours générer des exceptions. Mais je me sens comme des problèmes tout à fait raisonnables, par exemple, une entrée utilisateur qui n’analyse pas est essentiellement un "flux de contrôle" et ne devrait probablement pas renvoyer d’exception.
Pensées suivantes:
Ce que je retiens actuellement de cette discussion est le suivant:
- Si vous vous attendez à ce que l'appelant immédiat
catch
traite et gère l'exception la plupart du temps et poursuive son travail, peut-être par un autre chemin, elle devrait probablement faire partie du type de retour.Optional
ouOneOf
peut être utile ici. - Si vous vous attendez à ce que l'appelant immédiat ne détecte pas l'exception la plupart du temps, lancez une exception pour éviter la stupidité de la transmettre manuellement dans la pile.
- Si vous n'êtes pas sûr de ce que l'appelant immédiat va faire, fournissez peut-être les deux, comme
Parse
etTryParse
.
la source
Result
;-)FileNotFoundException
.Either
monade n'est pas un code d'erreur et qu'elle ne l'est pas non plusOneOf
? Elles sont fondamentalement différentes et la question semble donc reposer sur un malentendu. (Bien que sous une forme modifiée, c'est toujours une question valable.)Réponses:
C'est très certainement une bonne chose.
Vous voulez que tout ce qui laisse le système dans un état non défini arrête le système car un système non défini peut faire des choses désagréables comme des données corrompues, formater le disque dur et envoyer des courriers électroniques menaçants au président. Si vous ne pouvez pas restaurer le système et le remettre dans un état défini, il est responsable de la panne. C'est exactement pourquoi nous construisons des systèmes qui plantent plutôt que de se déchirer tranquillement. Bien sûr, nous voulons tous un système stable qui ne se bloque jamais, mais nous ne le souhaitons vraiment que lorsque le système reste dans un état de sécurité prévisible et défini.
C'est absolument vrai, mais c'est souvent mal compris. Lorsqu'ils ont inventé le système d'exception, ils craignaient de rompre la programmation structurée. La programmation structurée est pourquoi nous avons
for
,while
,until
,break
etcontinue
quand tout ce que nous devons, de faire tout cela, estgoto
.Dijkstra nous a appris que l'utilisation de goto de manière informelle (c'est-à-dire que vous pouvez sauter où bon vous semble) fait de la lecture de code un cauchemar. Quand ils nous ont donné le système d'exception, ils ont eu peur de réinventer la méthode goto. Alors ils nous ont dit de ne pas "l'utiliser pour le contrôle de flux" en espérant que nous comprendrions. Malheureusement, beaucoup d'entre nous n'ont pas.
Bizarrement, nous n'utilisons pas souvent les exceptions pour créer du code spaghetti comme nous le faisions auparavant avec goto. Le conseil lui-même semble avoir causé plus de problèmes.
Fondamentalement, les exceptions consistent à rejeter une hypothèse. Lorsque vous demandez la sauvegarde d’un fichier, vous supposez qu’il peut et sera sauvegardé. L'exception que vous obtenez quand il ne peut pas être peut-être sur le nom étant illégal, le HD étant plein, ou parce qu'un rat a rongé à travers votre câble de données. Vous pouvez gérer toutes ces erreurs différemment, de la même manière ou les laisser arrêter le système. Il y a un chemin heureux dans votre code où vos hypothèses doivent être vérifiées. D'une manière ou d'une autre, des exceptions vous éloignent de cette voie heureuse. Strictement parlant, oui, c'est une sorte de "contrôle de flux", mais ce n'est pas pour cela qu'ils vous ont mis en garde. Ils parlaient de bêtises comme ceci :
"Les exceptions devraient être exceptionnelles". Cette petite tautologie est née car les concepteurs de systèmes d'exception ont besoin de temps pour créer des traces de pile. Comparé aux sauts, c'est lent. Il mange du temps de calcul. Mais si vous êtes sur le point de vous connecter et d’arrêter le système ou du moins d’interrompre le traitement qui prend beaucoup de temps avant de commencer le suivant, vous avez un peu de temps à perdre. Si les gens commencent à utiliser des exceptions "pour le contrôle de flux", ces hypothèses sur le temps disparaissent toutes. Donc, "Les exceptions devraient être exceptionnelles" nous a vraiment été donné comme une considération de performance.
Bien plus important que cela ne nous confond pas. Combien de temps vous a-t-il fallu pour repérer la boucle infinie dans le code ci-dessus?
... est un bon conseil lorsque vous vous trouvez dans une base de code qui n'utilise généralement pas de codes d'erreur. Pourquoi? Parce que personne ne se souviendra de sauvegarder la valeur de retour et de vérifier vos codes d'erreur. C'est toujours une bonne convention quand on est en C.
Vous utilisez encore une autre convention. C'est bien tant que vous établissez la convention et que vous ne vous contentez pas d'en combattre une autre. Il est déroutant d'avoir deux conventions d'erreur dans la même base de code. Si, d'une manière ou d'une autre, vous vous êtes débarrassé de tout le code qui utilise l'autre convention, continuez.
J'aime la convention moi-même. Une des meilleures explications que j'ai trouvées ici * :
Mais même si cela me plaît, je ne vais pas encore le mélanger avec les autres conventions. Choisissez en un et gardez le. 1
1: Je veux dire par là ne me fait pas penser à plus d’une convention à la fois.
Ce n'est vraiment pas si simple. Une des choses fondamentales que vous devez comprendre est ce qu'est un zéro.
Combien de jours reste-t-il en mai? 0 (parce que ce n'est pas mai, c'est déjà juin).
Les exceptions sont un moyen de rejeter une hypothèse, mais ce n'est pas le seul moyen. Si vous utilisez des exceptions pour rejeter l’hypothèse, vous quittez la bonne voie. Mais si vous choisissez des valeurs pour indiquer le chemin heureux qui signale que les choses ne sont pas aussi simples qu'on le supposait, vous pouvez rester sur ce chemin tant qu'il peut gérer ces valeurs. Parfois, 0 est déjà utilisé pour signifier quelque chose, vous devez donc trouver une autre valeur pour cartographier votre hypothèse de rejet de l'idée. Vous pouvez reconnaître cette idée de son utilisation dans la bonne vieille algèbre . Les monades peuvent aider, mais il ne faut pas toujours que ce soit une monade.
Par exemple 2 :
Pouvez-vous penser à une bonne raison pour que cela soit conçu de manière à ce qu'il jette délibérément quoi que ce soit? Devinez ce que vous obtenez quand aucun int ne peut être analysé? Je n'ai même pas besoin de te dire.
C'est un signe d'un bon nom. Désolé, TryParse n'est pas mon idée d'un bon nom.
Nous évitons souvent de faire une exception pour ne rien obtenir alors que la réponse pourrait être plus d'une chose à la fois, mais pour une raison quelconque, si la réponse est une chose ou rien, nous sommes obsédés d'insister pour qu'elle nous donne une chose ou un jet:
Les lignes parallèles doivent-elles vraiment causer une exception ici? Est-ce vraiment si grave que cette liste ne contienne jamais plus d'un point?
Peut-être que, sémantiquement, vous ne pouvez pas supporter ça. Si c'est le cas, c'est dommage. Mais peut-être que les monades, qui n'ont pas une taille arbitraire
List
, vous feront vous sentir mieux.Les Monads sont de petites collections à usage spécial conçues pour être utilisées de manière spécifique, sans avoir à les tester. Nous sommes censés trouver des moyens de les gérer, peu importe ce qu’ils contiennent. De cette façon, le chemin heureux reste simple. Si vous ouvrez et testez chaque Monad que vous touchez, vous l'utiliserez mal.
Je sais, c'est bizarre. Mais c'est un nouvel outil (enfin, pour nous). Alors donnez-lui du temps. Les marteaux ont plus de sens lorsque vous cessez de les utiliser avec des vis.
Si vous me le permettez, j'aimerais répondre à ce commentaire:
C'est absolument vrai. Les monades sont beaucoup plus proches des collections que les exceptions, les drapeaux ou les codes d'erreur. Ils fabriquent de beaux récipients pour de telles choses quand ils sont utilisés judicieusement.
la source
throw
je ne sais pas vraiment ou ne pas dire où nous allons sauter;)C # n'est pas Haskell et vous devriez suivre le consensus des experts de la communauté C #. Si vous essayez plutôt de suivre les pratiques Haskell dans un projet C #, vous aliénerez tous les autres membres de votre équipe et vous découvrirez probablement les raisons pour lesquelles la communauté C # fait les choses différemment. Une des principales raisons est que C # ne soutient pas convenablement les syndicats discriminés.
Ce n'est pas une vérité universellement acceptée. Le choix dans chaque langue qui prend en charge les exceptions est soit de lancer une exception (que l'appelant est libre de ne pas gérer), soit de retourner une valeur composée (que l'appelant DOIT gérer).
La propagation des conditions d'erreur à travers la pile d'appels requiert une condition à chaque niveau, ce qui double la complexité cyclomatique de ces méthodes et par conséquent le nombre de tests unitaires. Dans les applications métier classiques, de nombreuses exceptions vont au-delà de la récupération et peuvent se propager au niveau le plus élevé (par exemple, le point d'entrée de service d'une application Web).
la source
Vous êtes arrivé à C # à un moment intéressant. Jusqu'à récemment, le langage était fermement ancré dans l'espace de programmation impératif. L'utilisation d'exceptions pour communiquer des erreurs était définitivement la norme. L'utilisation de codes de retour souffrait d'un manque de prise en charge linguistique (par exemple autour des unions discriminées et des correspondances de modèle). Assez raisonnablement, les directives officielles de Microsoft consistaient à éviter les codes de retour et à utiliser des exceptions.
Il y a toujours eu des exceptions à cela, toutefois, sous la forme de
TryXXX
méthodes, qui renvoient unboolean
résultat de succès et fournissent un résultat de seconde valeur via unout
paramètre. Celles-ci sont très similaires aux modèles "try" de la programmation fonctionnelle, sauf que le résultat provient d'un paramètre out plutôt que d'uneMaybe<T>
valeur de retour.Mais les choses changent. La programmation fonctionnelle devient de plus en plus populaire et des langages tels que C # répondent. C # 7.0 a introduit certaines fonctionnalités de correspondance de modèle de base dans le langage; C # 8 en introduira beaucoup plus, y compris une expression de commutateur, des modèles récursifs, etc. Parallèlement, les "bibliothèques fonctionnelles" pour C # se sont multipliées, telles que ma propre bibliothèque Succinc <T> , qui prend également en charge les syndicats discriminés. .
Ces deux éléments combinés signifient que la popularité du code comme celui-ci augmente lentement.
C'est encore assez une niche pour le moment, même si au cours des deux dernières années, j'ai constaté un net changement de l'hostilité générale des développeurs C # vers ce type de code et un intérêt croissant pour l'utilisation de telles techniques.
Nous ne sommes pas encore là pour dire " NE retournez PAS les codes d'erreur". est désuet et doit prendre sa retraite. Mais la marche vers la réalisation de cet objectif est bien engagée. Alors, faites votre choix: restez fidèles aux anciennes méthodes de rejet des exceptions avec abandon gay si vous aimez faire les choses comme vous le souhaitez. ou commencez à explorer un nouveau monde où les conventions des langages fonctionnels sont de plus en plus populaires en C #.
la source
java.util.Stream
(un IEnumerable-semblable à moitié assod) et lambdas maintenant, non? :)Je pense qu'il est parfaitement raisonnable d'utiliser un DU pour renvoyer des erreurs, mais je dirais alors que j'ai écrit OneOf :) Mais je dirais quand même, car c'est une pratique courante en F #, etc. ).
Je ne pense pas que les erreurs que je retourne en général sont des situations exceptionnelles, mais plutôt normales, mais j'espère que rarement rencontrées des situations qui peuvent, ou ne doivent pas nécessairement être traitées, mais doivent être modélisées.
Voici l'exemple de la page du projet.
Lancer des exceptions pour ces erreurs semble exagéré - elles ont une surcharge de performances, outre les inconvénients de ne pas être explicite dans la signature de la méthode, ou de fournir une correspondance exhaustive.
Dans quelle mesure OneOf est idiomatique en c #, c'est une autre question. La syntaxe est valide et raisonnablement intuitive. Les DU et la programmation orientée rail sont des concepts bien connus.
Je voudrais sauver des exceptions pour des choses qui indiquent que quelque chose est cassé lorsque cela est possible.
la source
OneOf
s’agit de rendre la valeur de retour plus riche, comme de retourner un Nullable. Il remplace les exceptions pour les situations qui ne sont pas exceptionnelles pour commencer.OneOf
est à peu près équivalent aux exceptions vérifiées en Java. Il y a quelques différences:OneOf
ne fonctionne pas avec les méthodes produisant des méthodes appelées pour leurs effets secondaires (car elles peuvent être appelées de manière significative et le résultat simplement ignoré). Évidemment, nous devrions tous essayer d'utiliser des fonctions pures, mais ce n'est pas toujours possible.OneOf
ne fournit aucune information sur l'endroit où le problème s'est produit. Cela est utile car cela permet d'économiser les performances (les exceptions émises en Java sont coûteuses en raison du remplissage de la trace de pile et en C #, ce sera la même chose) et vous oblige à fournir suffisamment d'informations dans le membre d'erreur deOneOf
lui - même. C'est également mauvais, car ces informations peuvent être insuffisantes et vous pouvez avoir du mal à trouver l'origine du problème.OneOf
et exception cochée ont une "fonctionnalité" importante en commun:Cela empêche votre peur
mais comme déjà dit, cette peur est irrationnelle. En gros, il y a généralement une seule place dans votre programme, où vous devez tout attraper (il est probable que vous utilisiez déjà un framework pour le faire). Comme vous et votre équipe passez généralement des semaines ou des années à travailler sur le programme, vous ne l'oublierez pas.
Le gros avantage des exceptions ignorables est qu'elles peuvent être gérées n'importe où, plutôt que partout dans la trace de pile. Cela élimine des tonnes de passe-partout (il suffit de regarder du code Java déclarant des "lancers ...." ou des exceptions d'encapsulation) et cela rend également votre code moins bogué: Comme toute méthode peut lancer, vous devez en être conscient. Heureusement, la bonne chose à faire est généralement de ne rien faire , c'est-à-dire de la laisser bouillonner jusqu'à un endroit où elle peut être traitée de manière raisonnable.
la source
OneOf<SomeSuccess, SomeErrorInfo>
oùSomeErrorInfo
fournit les détails de l'erreur.Les erreurs ont un certain nombre de dimensions. J'identifie trois dimensions: peuvent-elles être empêchées, à quelle fréquence, et si elles peuvent être récupérées. Pendant ce temps, la signalisation d'erreur a principalement une dimension: décider dans quelle mesure battre l'appelant au-dessus de la tête pour le forcer à gérer l'erreur ou laisser l'erreur "discrètement" s'afficher comme une exception.
Les erreurs non évitables, fréquentes et récupérables sont celles qui doivent vraiment être traitées sur le site de l'appel. Ils justifient le mieux de forcer l'appelant à les confronter. En Java, je leur fais des exceptions vérifiées, et un effet similaire est obtenu en leur rendant des
Try*
méthodes ou en renvoyant une union discriminée. Les unions discriminées sont particulièrement utiles lorsqu'une fonction a une valeur de retour. Ils sont une version beaucoup plus raffinée du retournull
. La syntaxe destry/catch
blocs n'est pas bonne (trop de crochets), ce qui rend les alternatives plus belles. De plus, les exceptions sont légèrement lentes car elles enregistrent des traces de pile.Les erreurs évitables (qui n'ont pas été évitées en raison d'une erreur ou d'une négligence du programmeur) et les erreurs non récupérables fonctionnent vraiment bien comme exceptions ordinaires. Les erreurs peu fréquentes fonctionnent également mieux en tant qu'exceptions ordinaires, puisqu'un programmeur peut souvent penser que cela ne vaut pas la peine d'anticiper (cela dépend de l'objectif du programme).
Un point important est que c’est souvent le site d’utilisation qui détermine la manière dont les modes d’erreur de la méthode s’adaptent à ces dimensions, ce qui doit être pris en compte lors de la prise en compte des éléments suivants.
D'après mon expérience, forcer l'appelant à faire face aux conditions d'erreur est acceptable lorsque les erreurs sont évitables, fréquentes et récupérables, mais cela devient très agaçant lorsque l'appelant est obligé de sauter à travers des cerceaux lorsqu'il n'en a pas besoin ou ne le souhaite pas . Cela peut être dû au fait que l'appelant sait que l'erreur ne se produira pas ou parce qu'il ne veut pas (encore) rendre le code résistant. En Java, cela explique l'angoisse suscitée par le recours fréquent aux exceptions vérifiées et au renvoi de Optional (similaire à Maybe et qui a récemment été introduit malgré de nombreuses controverses). En cas de doute, lancez des exceptions et laissez l'appelant décider de la façon dont il souhaite les gérer.
Enfin, gardez à l'esprit que la chose la plus importante concernant les conditions d'erreur, bien au-delà de toute autre considération telle que la manière dont elles sont signalées, est de les documenter de manière approfondie .
la source
Les autres réponses ont traité des exceptions par rapport aux codes d'erreur de manière suffisamment détaillée. J'aimerais donc ajouter une autre perspective spécifique à la question:
OneOf n'est pas un code d'erreur, c'est plutôt une monade
La manière dont il est utilisé
OneOf<Value, Error1, Error2>
est un conteneur qui représente le résultat réel ou un état d'erreur. C'est comme unOptional
, sauf que lorsqu'aucune valeur n'est présente, cela peut fournir plus de détails.Le principal problème avec les codes d'erreur est que vous oubliez de les vérifier. Mais ici, vous ne pouvez littéralement pas accéder au résultat sans vérifier les erreurs.
Le seul problème est que vous ne vous souciez pas du résultat. L'exemple de la documentation de OneOf donne:
... puis ils créent des réponses HTTP différentes pour chaque résultat, ce qui est bien mieux que d'utiliser des exceptions. Mais si vous appelez la méthode comme ça.
alors vous n'avez un problème et exceptions serait le meilleur choix. Comparer Rail vs .
save
save!
la source
Une chose à considérer est que le code en C # n'est généralement pas pur. Dans une base de code pleine d'effets secondaires et avec un compilateur qui se fiche de ce que vous faites avec la valeur de retour d'une fonction, votre approche peut vous causer beaucoup de chagrin. Par exemple, si vous avez une méthode qui supprime un fichier, vous voulez vous assurer que l'application avertit quand la méthode a échoué, même s'il n'y a aucune raison de vérifier le code d'erreur "sur le chemin heureux". C'est une différence considérable par rapport aux langages fonctionnels qui vous obligent à ignorer explicitement les valeurs de retour que vous n'utilisez pas. En C #, les valeurs de retour sont toujours implicitement ignorées (la seule exception étant les getters de propriétés IIRC).
La raison pour laquelle MSDN vous dit de "ne pas renvoyer de codes d'erreur" est exactement la même: personne ne vous oblige même à lire le code d'erreur. Le compilateur ne vous aide pas du tout. Cependant, si votre fonction est exempte d’effets secondaires, vous pouvez utiliser en toute sécurité quelque chose comme
Either
: le fait est que même si vous ignorez le résultat de l’erreur (le cas échéant), vous ne pouvez le faire que si vous n’utilisez pas le "résultat correct" Soit. Il y a quelques façons comment vous pouvez y parvenir - par exemple, vous pouvez autoriser uniquement les « lire » la valeur en passant un délégué pour gérer le succès et les cas d'erreur, et vous pouvez facilement combiner avec des approches telles que la programmation orientée des chemins de fer (il ne fonctionne très bien en C #).int.TryParse
est quelque part au milieu. C'est pur Il définit les valeurs des deux résultats à tout moment. Vous savez donc que si la valeur renvoyée estfalse
, le paramètre de sortie sera défini sur0
. Cela ne vous empêche toujours pas d'utiliser le paramètre de sortie sans vérifier la valeur de retour, mais au moins, vous êtes assuré du résultat, même si la fonction échoue.Mais une chose absolument cruciale ici est la cohérence . Le compilateur ne va pas vous sauver si quelqu'un change cette fonction pour avoir des effets secondaires. Ou jeter des exceptions. Ainsi, bien que cette approche fonctionne parfaitement en C # (et que je l'utilise bien), vous devez vous assurer que tous les membres de votre équipe la comprennent et l'utilise . La plupart des invariants nécessaires au bon fonctionnement de la programmation fonctionnelle ne sont pas appliqués par le compilateur C # et vous devez vous assurer qu'ils sont suivis par vous-même. Si vous ne respectez pas les règles que Haskell applique par défaut, vous devez vous tenir au courant de ce qui ne fonctionne pas. Vous devez en tenir compte pour tous les paradigmes fonctionnels que vous introduisez dans votre code.
la source
La déclaration correcte serait Ne pas utiliser les codes d'erreur pour les exceptions! Et n'utilisez pas d'exceptions pour des non-exceptions.
Si quelque chose arrive que votre code ne peut pas récupérer (c'est-à-dire le faire fonctionner), c'est une exception. Votre code immédiat ne ferait rien avec un code d'erreur, si ce n'est de le remonter pour le connecter ou peut-être de fournir le code à l'utilisateur d'une manière ou d'une autre. Cependant, c’est précisément à cela que servent les exceptions - elles traitent de tous les problèmes et aident à analyser ce qui s’est réellement passé.
Cependant, si quelque chose se produit, telle qu'une entrée invalide que vous pouvez gérer, soit en la reformatant automatiquement, soit en demandant à l'utilisateur de corriger les données (et vous y avez pensé), ce n'est pas vraiment une exception, car attendez-le. Vous pouvez vous sentir libre de gérer ces cas avec des codes d'erreur ou toute autre architecture. Juste pas des exceptions.
(Notez que je ne suis pas très expérimenté en C #, mais ma réponse devrait être valable dans le cas général en fonction du niveau conceptuel des exceptions.)
la source
catch
souhaitaient pas que des exceptions soient utilisées pour les erreurs récupérables, le mot clé n'existerait pas. Et comme nous parlons dans un contexte C #, il convient de noter que la philosophie adoptée ici n’est pas suivie par le framework .NET; L’exemple le plus simple et imaginable de «saisie invalide que vous puissiez gérer» est peut- être l’utilisateur qui saisit un non-nombre dans un champ où un nombre est attendu ... etint.Parse
lève une exception.C'est une "Terrible Idea".
C'est terrible parce que, du moins dans .net, vous ne disposez pas d'une liste exhaustive des exceptions possibles. N'importe quel code peut éventuellement renvoyer une exception. Surtout si vous faites de la programmation orientée objet et pouvez appeler des méthodes substituées sur un sous-type plutôt que sur le type déclaré
Vous devrez donc modifier toutes les méthodes et fonctions
OneOf<Exception, ReturnType>
, puis, si vous souhaitez gérer différents types d'exceptions, vous devrez examiner le type,If(exception is FileNotFoundException)
etc.try catch
est l'équivalent deOneOf<Exception, ReturnType>
Modifier ----
Je suis conscient que vous proposez de revenir,
OneOf<ErrorEnum, ReturnType>
mais j'estime qu'il s'agit d'une distinction sans différence.Vous combinez les codes de retour, les exceptions attendues et les branches par exception. Mais l'effet global est le même.
la source
Exception
art.