Existe-t-il un atsortingbut standard «Ne retourne jamais» pour les fonctions C #?

J’ai une méthode qui ressemble à ceci:

void throwException(ssortingng msg) { throw new MyException(msg); } 

Maintenant si j’écris

 int foo(int x, y) { if (y == 0) throwException("Doh!"); else return x/y; } 

le compilateur se plaindra de foo que “tous les chemins ne renvoient pas de valeur”.

Y a-t-il un atsortingbut que je peux append à throwException pour éviter cela? Quelque chose comme:

 [NeverReturns] void throwException(ssortingng msg) { throw new MyException(msg); } 

Je crains que les atsortingbuts personnalisés ne fassent pas l’affaire, car pour ce faire, j’aurais besoin de la coopération du compilateur.

    Non, je vous suggère de modifier la signature de votre première fonction pour renvoyer l’exception plutôt que de la renvoyer, et de laisser l’instruction de projection dans votre deuxième fonction. Le compilateur sera heureux et sentira moins mauvais.

    Pourquoi ne pas simplement changer pour

     int foo(int x, y) { if (y == 0) throwException("Doh!"); return x/y; } 

    Cela donne les mêmes résultats d’exécution et le compilateur ne se plaindra pas.

    La réponse de Bernhof est correcte. Cependant, si vous essayez d’encapsuler une grande partie de la logique lors de l’instanciation de votre exception, il vous suffira alors de changer votre code:

     void throwException(ssortingng msg) { throw new MyException(msg); } 

    pour ça:

     Exception makeException(ssortingng msg) { return new MyException(msg); } 

    Ensuite, votre code d’appel ressemblera à ceci:

     int foo(int x, y) { if (y == 0) { throw makeException("Doh!"); } return x / y; } 

    Toutes choses étant égales par ailleurs , préférez le code fonctionnel au code de procédure. Il est plus facile de réutiliser et de tester un peu.

    MODIFIER:

    À la lumière de l’exemple de code de Fred, voici ce que je ferais. Ce n’est pas un contrat de code, mais c’est toujours fonctionnel.

     private int getVarID(ssortingng s_varID) { int varID; if(s_varID == "ILT") { return 123; } else if(s_varID == "TL") { return 456; } else if(s_varID == "FT") { return 789; } else if(int.TryParse(s_varID, out varID)) { return varID; } else { throw makeParseError("varID must be an integer or 'ILT', 'TL' or 'FT'."); } } 

    Vous pouvez indiquer “ne retourne jamais” en utilisant des génériques pour déclarer que la fonction renvoie “tout”:

     T ThrowException(ssortingng msg) { throw new MyException(msg); } 

    Alors maintenant, vous pouvez écrire:

     int foo(int x, int y) { if (y == 0) return ThrowException("Doh!"); else return x/y; } 

    Cet idiome est utilisé dans des langages tels que Haskell et F # , et est basé sur le principe de l’explosion , également connu sous le nom “ex falso quodlibet”. Le raisonnement est le suivant: si une fonction ne retourne jamais, alors nous pouvons émettre toutes les hypothèses magiques que nous voulons sur sa valeur de retour, car une telle valeur n’existera jamais. Ici, l’appelant (foo) suppose que ThrowException renverra un int.

    Quelques inconvénients mineurs:

    • L’implémentation de ThrowException peut contourner ce ThrowException en renvoyant default(T) .
    • Vous devez spécifier le type de retour lors de l’appel de ThrowException (Haskell et F # peuvent l’inférer).
    • Cet idiome est très rare en C #, tant de personnes ne le reconnaîtront pas. Vous devrez peut-être append un commentaire indiquant ce que vous faites.

    Comme le disent les autres réponses, vous feriez probablement mieux de renvoyer l’exception plutôt que de la lancer.

    Ne confiez pas la création d’exception à une autre fonction (c’est-à-dire lancez-la directement) et le compilateur ne se plaindra pas. Passer à une fonction de type “helper” pour lancer une exception est une perte de temps, sauf si la fonction ajoute de la valeur au processus d’exception.

    Votre fonction

     void throwException(ssortingng msg) { throw new MyException(msg); } 

    append zéro valeur au code, donc votre question est sans object. Si, par contre, vous voulez envoyer une erreur avec le même message dans toute la classe et minimiser la duplication de code, vous devez le faire.

    La pratique normale consisterait à prolonger MyException pour ce cas particulier et à lancer ceci:

     public class HomerSimpsonException : MyException { public HomerSimpsonException() : base ("DOH!!!"){ } } int foo(int x, y) { if (y == 0) throw new HomerSimpsonException(); else return x/y; } 

    Même dans ce cas, ce n’est pas assez complet selon la règle de Microsoft pour l’extension des exceptions, vous devez implémenter un minimum de 4 constructeurs – http://msdn.microsoft.com/en-us/library/ms182151%28VS.80%29.aspx , à savoir:

      public NewException(){} public NewException(ssortingng){} public NewException(ssortingng, Exception){} protected or private NewException(SerializationInfo, StreamingContext){} 

    Bernhof vous a déjà donné un moyen d’éviter que le compilateur se plaint. Toutefois, sachez également que votre trace de stack sera désactivée (et que certaines bibliothèques de consignateurs ne gèrent pas les méthodes util-classed-i-throw-exceptions-for-your-app), ce qui complique le débogage de votre application.

    Eh bien, ceci est une “jolie” implémentation nécessitant peu d’effort.

    La classe ouvrière:

     ///  /// Representation of an unreachable type, exposing a method to represent unreachable code. ///  public static class Unreachable { ///  /// Representation of unreachable code with return semantics. ///  public static dynamic Code() { throw new NotImplementedException(@"Unreachable code was reached."); } } 

    Exemple:

     public object[] UnreachableCodeTest() { return Unreachable.Code(); } 

    Décompilé:

     Offset OpCode Operand 0 ldsfld System.Runtime.ComstackrServices.CallSite`1> TestApp.Program/o__SiteContainere6::<>p__Sitee7 5 brtrue.s -> (10) ldsfld System.Runtime.ComstackrServices.CallSite`1> TestApp.Program/o__SiteContainere6::<>p__Sitee7 7 ldc.i4.0 8 ldtoken System.Object[] 13 call System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle) 18 ldtoken TestApp.Program 23 call System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle) 28 call System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder::Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,System.Type,System.Type) 33 call System.Runtime.CompilerServices.CallSite`1 System.Runtime.ComstackrServices.CallSite`1>::Create(System.Runtime.ComstackrServices.CallSiteBinder) 38 stsfld System.Runtime.ComstackrServices.CallSite`1> TestApp.Program/o__SiteContainere6::<>p__Sitee7 43 ldsfld System.Runtime.CompilerServices.CallSite`1> TestApp.Program/o__SiteContainere6::<>p__Sitee7 48 ldfld !0 System.Runtime.CompilerServices.CallSite`1>::Target 53 ldsfld System.Runtime.ComstackrServices.CallSite`1> TestApp.Program/o__SiteContainere6::<>p__Sitee7 58 call System.Object TestApp.Unreachable::Code() 63 callvirt !2 System.Func`3::Invoke(!0,!1) 68 ret 

    Optimisez en implémentant quelque chose avec Fody, en recherchant cette signature d’appel et en la remplaçant par une seule retouche brute (si elle vous le permet) ou une autre alternative simple, acceptable, à faible coût.

    (Modifier)

    Au fur et à mesure que vous me modifiez, je vais être obligé d’expliquer pourquoi c’est efficace et utile.

    Généralement, cela va dans un bloc qui ressemble à la fin d’une méthode qui a un chemin logiquement inaccessible;

     #pragma warning disable 162 // ReSharper disable CSharpWarnings::CS0162 // ReSharper disable HeuristicUnreachableCode return Unreachable.Code(); // ReSharper restore HeuristicUnreachableCode // ReSharper restore CSharpWarnings::CS0162 #pragma warning restore 162 

    Si vous modifiez le code au-dessus de cette section de manière à ce que le code soit accessible, vos tests échoueront. Si vous avez du code ci-dessous, vous vous trompez et le compilateur vous le fera savoir. Une fois que vous avez dépassé la maturité initiale, vous supprimez ce bloc de code. Le but principal de cette opération est d’attraper des scénarios dans lesquels le code n’est pas inaccessible quand il le devrait.

    Dans d’autres scénarios où le code ne devrait pas arriver mais ne peut pas être exclu logiquement par le compilateur (une instruction par défaut dans un cas, par exemple), vous générez généralement une erreur. Si vous souhaitez optimiser ce scénario, vous avez besoin de ce type d’implémentation.

     select( thisIsAlways123Never0OrGreaterThan3 ) { default: return Unreachable.Code(); case 1: DoSomething(); break; case 2: DoSomethingElse(); break; case 3: return GetSomething(); } 

    Pour optimiser les instructions minimales émises pour le chemin par default: path avec ce code minimalement écrit, vous avez besoin d’un ami nommé Fody.

    Le scénario idéal est un résultat similaire à ce que les développeurs C ++ souhaitent dans GCC & LLVM __builtin_unreachable() ou __assume(0) ou __declspec(noreturn) sur une méthode vide.

    Supprimez le mot clé ‘else’, il est redondant de toute façon et cela fonctionnera;)