Quelqu'un utilise-t-il encore [goto] en C # et si oui, pourquoi? [fermé]

104

Je me demandais si quelqu'un utilise toujours la syntaxe du mot-clé "goto" en C # et quelles sont les raisons possibles de le faire.

J'ai tendance à considérer toutes les déclarations qui poussent le lecteur à parcourir le code comme une mauvaise pratique, mais je me demande s'il existe des scénarios crédibles pour utiliser une telle syntaxe.

Aller à la définition des mots-clés

Brian Scott
la source
3
que voulez-vous dire "encore"? Y a-t-il eu une période pendant laquelle les gens l'utilisaient tout le temps [en c #]?
Massif
4
@Massif: "still" était destiné à souligner l'opinion moderne de l'utilisation de "goto" comme prélude au code spaghetti et un manque de lisibilité dans le code source. Très rarement, vous voyez des exemples de code incluant ce mot-clé particulier, raison pour laquelle j'étais intéressé à le demander en premier lieu.
Brian Scott
50
Si le lecteur «saute» le code est une mauvaise pratique, évitez-vous également de «casser», «continuer», «lancer» et «retourner»? Ils provoquent tous une branche dans le flux de contrôle, parfois une branche non locale. "Throw" ne vous dit même pas où il va, contrairement à goto.
Eric Lippert
2
J'utilise gotopour briser une boucle et revenir à la déclaration de départ selon une condition spécifique
Nitin Sawant
3
J'aime aller. J'ai essayé de l'éviter à cause de la tendance des gens à l'éviter car cela rend le code plus difficile à lire. Après avoir appris le langage d'assemblage et les instructions de branche, je pense que parfois, cela peut rendre le code plus lisible. Je pense que plusieurs utilisations dans une méthode et sauter trop loin dans le code peuvent faire plus de mal que de bien. Mais si vous pensez qu'un goto fonctionnerait bien ici, un simple goto de temps en temps ne devrait pas vous faire sortir de votre chemin pour éviter simplement parce que le consensus commun est de l'éviter.
eaglei22

Réponses:

93

Il existe quelques (rares) cas où goto peut améliorer la lisibilité. En fait, la documentation que vous avez liée à deux exemples:

Une utilisation courante de goto consiste à transférer le contrôle vers une étiquette de cas de commutation spécifique ou l'étiquette par défaut dans une instruction de commutation.

L'instruction goto est également utile pour sortir des boucles profondément imbriquées.

Voici un exemple pour ce dernier:

for (...) {
    for (...) {
        ...
        if (something)
            goto end_of_loop;
    }
}

end_of_loop:

Bien sûr, il existe également d'autres moyens de contourner ce problème, comme la refactorisation du code dans une fonction, l'utilisation d'un bloc factice autour de lui, etc. (voir cette question pour plus de détails). En remarque, les concepteurs du langage Java ont décidé d'interdire complètement goto et d'introduire une instruction break étiquetée à la place.

Heinzi
la source
48
Normalement, j'essaierais de refactoriser cela pour mettre les boucles dans une méthode distincte dont je pourrais simplement revenir ...
Jon Skeet
2
@Heinzi - Je n'ai pas vu goto être justifié. Comme Jon le dit, si cela est "justifié", le code demande à être refactorisé.
manojlds
29
étiqueté pause, juste une façon plus longue de dire goto parce qu'il fait la même chose bizarre ....
Jesus Ramos
20
@Jesus mais avec goto, vous pouvez aller n'importe où. Une pause étiquetée garantit que vous sortez de la boucle.
mihsathe
1
À moins que vous ne soyez aventureux et que vous n'utilisiez goto avec une adresse (je l'ai déjà vu faire), ce problème est atténué. Et je doute que quelqu'un utilise des crochets de détour dans votre code C # et Java pour exploiter les déclarations goto.
Jesus Ramos
66

Je me souviens de cette partie

switch (a)     
{ 
    case 3: 
        b = 7;
        // We want to drop through into case 4, but C# doesn't let us
    case 4: 
        c = 3;
        break; 
    default: 
        b = 2;
        c = 4;
        break; 
}

À quelque chose comme ça

switch (a)     
{
    case 3: 
        b = 7;
        goto case 4;    
    case 4: 
        c = 3;
        break;     
    default: 
        b = 2;
        c = 4;
        break;
}

Référez ceci

V4Vendetta
la source
17
Je vois en fait cela comme la raison la plus valable d'utiliser [goto] pour le moment. Au moins dans ce scénario, cela augmente la lisibilité pour les programmeurs qui ne savent pas que les cas s'enchaînent sans instruction break.
Brian Scott
11
@Brian Scott, V4Vendetta. Sauf erreur de ma part, la première instruction ne se compile pas en C #. Cela aiderait le programmeur à comprendre.
Jodrell
2
V4Vendetta pourquoi avez-vous inséré une pause sur le premier extrait de code ...? Il valait mieux le montrer sans la pause, sinon les deux extraits font des choses différentes. La raison pour laquelle vous avez besoin du goto dans le deuxième exemple est précisément parce que le premier ne se compile pas en C # (comme il le ferait en C).
Stephen Holt
23

Je l'utilise abondamment dans Eduasync pour montrer le type de code que le compilateur génère pour vous lorsque vous utilisez des méthodes async en C # 5. Vous verriez la même chose dans les blocs d'itérateur.

En code "normal" cependant, je ne me souviens plus de la dernière fois que je l'ai utilisé ...

Jon Skeet
la source
1
pouvez-vous donner un petit exemple des raisons pour lesquelles cette approche a été préférée ou était-ce simplement une préférence personnelle?
Brian Scott
1
@Brian: Ce que vous voulez dire n'est pas vraiment clair. Eduasync montre le code C # équivalent à ce que le compilateur fait pour vous - et il génère du code qui utilise goto, effectivement ...
Jon Skeet
9

goto est idéal pour sortir de nombreuses boucles où break ne fonctionnerait pas bien (disons en cas d'erreur), et comme Kragen l'a dit, goto est utilisé par le compilateur pour générer des instructions switch et d'autres choses.

Jésus Ramos
la source
7
Sûrement "casser" / "continuer" sont de meilleures approches pour la gestion des boucles plutôt que d'exiger que l'éditeur de code saute autour du code source en essayant de comprendre où se déroule l'étape suivante?
Brian Scott
6
Pas si vous avez des boucles imbriquées.
Jesus Ramos
1
ok, je peux voir cela comme un scénario valide.
Brian Scott
1
En cas d'erreur, vous devez envisager de lever une exception.
Jodrell
6
Si vous souhaitez gérer l'erreur en interne sans exception, ce serait un moyen valable de le faire.
Jesus Ramos
8

Je ne me souviens pas avoir jamais utilisé goto. Mais peut-être que cela améliore l'intention d'une boucle éternelle que vous ne voulez vraiment jamais quitter (non break, mais vous pouvez toujours returnou throw):

forever: {
  // ...
  goto forever;
}

Là encore, un simple while (true)devrait suffire ...

Aussi, vous pouvez utiliser dans une situation où vous voulez que la première itération d'une boucle commence au milieu de la boucle: regardez ici pour un exemple.

Jordão
la source
La réponse liée contient une utilisation "intéressante" de goto.. et while(true) {..}n'est pas une utilisation intéressante ..
user2864740
5

Le compilateur utilise des gotoinstructions dans divers morceaux de code généré, par exemple dans les types de blocs d'itérateur générés (générés lors de l'utilisation du yield returnmot-clé - je suis presque sûr que les types de sérialisation XML générés contiennent également quelques gotoinstructions quelque part.

Voir Détails de l'implémentation du bloc Iterator: machines à états générées automatiquement pour plus de détails sur pourquoi / comment le compilateur C # gère cela.

À part le code généré, il n'y a pas de bonne raison d'utiliser une gotoinstruction dans du code normal - cela rend le code plus difficile à comprendre et par conséquent plus sujet aux erreurs. D'autre part en utilisantgoto instructions dans du code généré comme celui-ci peut simplifier le processus de génération et est normalement bien car personne ne va lire (ou modifier) ​​le code généré et il n'y a aucune chance que des erreurs soient commises car une machine est en train d'écrire.

Voir la déclaration Go-to considérée comme nuisible pour un argument contre gotoainsi qu'un morceau classique de l'histoire de la programmation.

Justin
la source
4
C'est stupide: il y a plusieurs cas où goto est utile, comme l'illustrent d'autres réponses. Soit vous les frottez explicitement, soit vous dites simplement "c'est faux" est, eh bien, faux.
o0 '.
@Lohoris Je ne l'achète pas - chaque exemple que j'ai vu où goto "améliore la lisibilité" (y compris les réponses ici) serait beaucoup plus lisible après une simple refactorisation.
Justin le
3
@Justin non, parfois les boucles imbriquées ne sont que la manière la plus naturelle de faire quelque chose, par exemple si vous parcourez un tableau de tableaux.
o0 '.
6
@Justin ce n'est pas toujours plus clair de l'intégrer dans une fonction. Vous forcez quelque chose (avoir une fonction) juste pour éviter quelque chose que vous détestez religieusement (en utilisant un goto). Indication claire de quelque chose de mal.
o0 '.
3
Les appels @Justin Functions ont une surcharge. gotone fait pas. Quelque chose à considérer.
Dan Bechard
2

Le processeur implémente au moins une instruction de saut et je suis sûr que beaucoup d'instructions utilisent celles-ci dans leur implémentation ou leur interprétation.

L'une des bonnes choses à propos de l'utilisation d'un langage de 3e ou 4e génération est que ces détails physiques nous sont abstraits. Alors que nous devrions être conscients de la loi de l'abstraction qui fuit, je pense que nous devrions également utiliser nos outils comme ils le sont ( désolé ). Si j'écrivais du code et que cela gotome semblait une bonne idée, il serait temps de refactoriser. Le but d'un langage structuré est d'éviter ces "sauts" et de créer un flux logique dans notre ingénierie.

Je devrais éviter l'utilisation de, breakmais je ne peux pas ignorer les avantages en termes de performances. Cependant, si j'ai des boucles imbriquées qui en ont besoin mutuellement, breakil est temps de refactoriser.

Si quelqu'un peut proposer une utilisation de gotocela semble mieux que la refactorisation, je retirerai volontiers ma réponse.

J'espère que je ne suis pas coupable de m'être précipité dans le "local à vélos " ici. Comme le dit Kragen, ce qui est assez bien pour Dijkstra me suffit.

Jodrell
la source
1
Prendre un dynamicobjet et parcourir son graphe d'objets qui contient plusieurs dictionnaires pour arriver aux valeurs dont j'ai besoin. Cela n'a pas de sens d'utiliser des méthodes qui ont un paramètre dynamicmais s'attendent à une forme d'objet exacte. Avec goto pour briser plusieurs couches et continuer à parcourir une collection de ces objets. [Je ne possède pas les types, donc je ne peux pas fournir un meilleur accès, donc réflexion ou dynamique, c'est]
Chris Marisic
-6

Goto n'est jamais mieux. Et continuez, break (sauf dans switch / case), (multiple) return, et throw devrait également être maintenu au minimum. Vous ne voulez jamais vous échapper du milieu des boucles de nidification. Vous voulez toujours que les instructions de contrôle de boucle aient tout le contrôle de boucle. La mise en retrait contient des informations, et toutes ces instructions jettent ces informations. Vous pouvez aussi bien supprimer toutes les mises en retrait.

Kirk Augustin
la source
10
Vous voudriez sortir d'une boucle s'il n'y a aucun intérêt à garder l'exécution de la boucle. Sinon, vous finirez par perdre plus de temps de traitement dans des boucles plus longues sans raison.
Skuld
12
@Kirk, cela ressemble à une opinion plutôt qu'à quelque chose de quantitatif?
Brian Scott