107 votes

Pourquoi une instruction "continue" ne peut-elle pas se trouver dans un bloc "finally" ?

Je n'ai pas de problème, je suis simplement curieux. Imaginez le scénario suivant :

foreach (var foo in list)
{
    try
    {
         //Some code
    }
    catch (Exception)
    {
        //Some more code
    }
    finally
    {
        continue;
    }
}

Cela ne compilera pas, car cela pose la question suivante erreur de compilation CS0157 :

Le contrôle ne peut pas quitter le corps d'une clause finale

Pourquoi ?

7 votes

Alors. Je suis juste curieux. Si vous comprenez parfaitement pourquoi il ne compile pas, pourquoi voulez-vous que quelqu'un vous explique ce qui a déjà du sens ? =)

14 votes

Pourquoi auriez-vous besoin d'un continue; sur finally bloc ? n'est-ce pas la même chose que continue; après le try -- catch bloc ?

0 votes

Probablement parce que ça ne sert à rien, c'est la dernière chose qui va arriver.

150voto

R. Martinho Fernandes Points 96873

finally s'exécutent, qu'une exception soit levée ou non. Si une exception est levée, qu'est-ce qui pourrait bien se passer ? continue faire ? Vous ne pouvez pas poursuivre l'exécution de la boucle, car une exception non attrapée transférera le contrôle à une autre fonction.

Même si aucune exception n'est levée, finally s'exécutera lorsque d'autres instructions de transfert de contrôle à l'intérieur du bloc try/catch s'exécuteront, comme une return par exemple, ce qui pose le même problème.

En bref, avec la sémantique de finally il n'est pas logique d'autoriser le transfert du contrôle depuis l'intérieur d'une finally à l'extérieur de celui-ci.

Soutenir cela avec une sémantique alternative serait plus déroutant qu'utile, puisqu'il existe des solutions de contournement simples qui rendent le comportement souhaité beaucoup plus clair. Vous obtenez donc une erreur, et vous êtes obligé de réfléchir correctement à votre problème. C'est l'idée générale de "vous jeter dans le gouffre du succès" qui prévaut en C#.

C#, you, and the out if success

Si vous souhaitez ignorer les exceptions (ce qui est le plus souvent une mauvaise idée) et poursuivre l'exécution de la boucle, utilisez un bloc catch all :

foreach ( var in list )
{
    try{
        //some code
    }catch{
        continue;
    }
}

Si vous voulez continue seulement lorsqu'aucune exception non attrapée n'est levée, il suffit de mettre continue en dehors du bloc d'essai.

12 votes

Je vais accepter cette réponse, parce que vous faites comprendre les raisons pour lesquelles Microsoft a peut-être décidé de ne pas accepter un continu dans un final. Probablement que ces images m'ont aussi convaincu :)

20 votes

Pouvez-vous développer l'idée de "vous jeter dans le gouffre du succès" ? Je n'ai pas compris :-D

9 votes

32voto

Mortalus Points 4030

Voici une source fiable :

Une instruction continue ne peut pas sortir d'un bloc finally (section 8.10). Lorsque une instruction continue se produit à l'intérieur d'un bloc finally, la cible de l'instruction de l'instruction continue doit se trouver dans le même bloc finally ; sinon, une erreur de compilation.

Il est extrait de MSDN, 8.9.2 L'instruction continue .

La documentation le dit :

Les instructions d'un bloc final sont toujours exécutées lorsque le contrôle quitte une instruction déclaration try. Ceci est vrai, que le transfert de contrôle se produise à la suite de à la suite d'une exécution normale, à la suite de l'exécution d'un break, l'exécution d'une instruction break, continue, goto ou return, ou de la propagation d'une exception hors de l'instruction try. Si une exception est levée pendant l'exécution d'un bloc final, l'exception est propagée à la prochaine instruction l'instruction try suivante qui l'entoure. Si une autre exception était en train d'être être propagée, cette exception est perdue. Le processus de propagation d'une d'une exception est abordé plus en détail dans la description de l'instruction throw (section 8.9.5).

Il est d'ici 8.10 L'instruction try .

31voto

Sriram Sakthivel Points 33463

Vous pouvez penser que c'est logique, mais c'est n'a pas de sens en fait.

foreach (var v in List)
{
    try
    {
        //Some code
    }
    catch (Exception)
    {
        //Some more code
        break; or return;
    }
    finally
    {
        continue;
    }
}

Que comptez-vous faire ? rupture ou un continuer lorsqu'une exception est levée ? L'équipe du compilateur C# ne veut pas prendre de décision par elle-même en supposant que break ou continue . Au lieu de cela, ils ont décidé de se plaindre de la situation du développeur sera ambiguë de transférer le contrôle de finally block .

C'est donc au développeur d'indiquer clairement ce qu'il a l'intention de faire plutôt qu'au compilateur de supposer autre chose.

J'espère que vous comprenez pourquoi cela ne compile pas !

0 votes

Dans le même ordre d'idées, même s'il n'y avait pas de "catch", le chemin d'exécution suivant un fichier finally serait affectée par le fait que le catch est sorti par une exception, et il n'y a pas de mécanisme pour dire comment cela devrait interagir avec une continue . J'aime bien votre exemple, cependant, car il montre un problème encore plus grand.

0 votes

@supercat D'accord avec vous, ma réponse montre un exemple de situation ambiguë pour le compilateur, mais il y a encore beaucoup de problèmes avec cette approche.

16voto

Chris Sinclair Points 14829

Comme d'autres l'ont dit, mais en se concentrant sur les exceptions, il s'agit en fait d'une gestion ambiguë du transfert de contrôle.

Dans votre esprit, vous pensez probablement à un scénario comme celui-ci :

public static object SafeMethod()
{
    foreach(var item in list)
    {
        try
        {
            try
            {
                //do something that won't transfer control outside
            }
            catch
            {
                //catch everything to not throw exceptions
            }
        }
        finally
        {
            if (someCondition)
                //no exception will be thrown, 
                //so theoretically this could work
                continue;
        }
    }

    return someValue;
}

Théoriquement, vous pouvez suivre le flux de contrôle et dire, oui, c'est "ok". Aucune exception n'est levée, aucun contrôle n'est transféré. Mais les concepteurs du langage C# avaient d'autres problèmes en tête.

L'exception rejetée

public static void Exception()
{
    try
    {
        foreach(var item in list)
        {
            try
            {
                throw new Exception("What now?");
            }
            finally
            {
                continue;
            }
        }
    }
    catch
    {
        //do I get hit?
    }
}

Le redoutable Goto

public static void Goto()
{
    foreach(var item in list)
    {
        try
        {
            goto pigsfly;
        }
        finally
        {
            continue;
        }
    }

    pigsfly:
}

Le retour

public static object ReturnSomething()
{
    foreach(var item in list)
    {
        try
        {
            return item;
        }
        finally
        {
            continue;
        }
    }
}

La rupture

public static void Break()
{
    foreach(var item in list)
    {
        try
        {
            break;
        }
        finally
        {
            continue;
        }
    }
}

Donc en conclusion, oui, bien qu'il y ait est a léger possibilité d'utiliser un continue dans les situations où le contrôle n'est pas transféré, mais une bonne partie (la majorité ?) des cas impliquent des exceptions ou des changements de comportement. return blocs. Les concepteurs du langage ont estimé que cela serait trop ambigu et (probablement) impossible à garantir au moment de la compilation que votre continue est utilisé uniquement dans les cas où le flux de contrôle n'est pas transféré.

0 votes

J'ai eu la même situation en java lorsque proguard se plantait pendant l'obfuscation. Votre explication est assez bonne. C# donne une erreur de compilation - c'est parfaitement logique.

11voto

ghord Points 2842

En général continue n'a pas de sens lorsqu'il est utilisé dans finally bloc. Jetez un coup d'oeil à ça :

foreach (var item in list)
{
    try
    {
        throw new Exception();
    }
    finally{
        //doesn't make sense as we are after exception
        continue;
    }
}

0 votes

"En général", beaucoup de choses n'ont pas de sens. Et ça ne veut pas dire que ça ne devrait pas fonctionner "en particulier". IE : continue n'a aucun sens en dehors de l'énoncé de la boucle, mais cela ne signifie pas qu'il n'est pas supporté.

0 votes

Je pense que ça a du sens. item peut être un fichier, lecture échouée -> finally ferme le fichier. continue empêche le reste du traitement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X