Est-il judicieux de créer des blocs uniquement pour réduire la portée d'une variable?

38

J'écris un programme en Java où, à un moment donné, je dois charger un mot de passe pour mon magasin de clés. Juste pour le plaisir, j'ai essayé de garder mon mot de passe en Java le plus court possible en procédant ainsi:

//Some code
....

KeyManagerFactory keyManager = KeyManagerFactory.getInstance("SunX509");
Keystore keyStore = KeyStore.getInstance("JKS");

{
    char[] password = getPassword();
    keyStore.load(new FileInputStream(keyStoreLocation), password);
    keyManager.init(keyStore, password);
}

...
//Some more code

Maintenant, je sais que dans ce cas, c'est un peu idiot. J'aurais pu faire beaucoup d'autres choses, la plupart d'entre elles étant meilleures (je n'aurais pas pu utiliser une variable du tout).

Cependant, j’étais curieux de savoir s’il existait un cas où cela n’était pas si stupide. La seule autre chose à laquelle je peux penser, c'est si vous voulez réutiliser des noms de variables communs tels que count, ou temp, mais de bonnes conventions de dénomination et des méthodes courtes rendent peu probable que cela soit utile.

Existe-t-il un cas où l’utilisation de blocs uniquement pour réduire la portée de la variable a du sens?

Lord Farquaad
la source
13
@gnat .... quoi? Je ne sais même pas comment modifier ma question pour qu'elle ressemble moins à cette cible. Quelle partie de cela vous a amené à croire que ces questions sont les mêmes?
Lord Farquaad
22
La réponse habituelle à cela est que votre bloc anonyme devrait être une méthode avec un nom significatif, ce qui vous donnera une documentation automatique et la portée réduite que vous vouliez. De prime abord, je ne peux pas penser à une situation où l'extraction d'une méthode n'est pas meilleure que l'insertion d'un bloc nu.
Kilian Foth
4
Quelques questions similaires demandent ceci pour C ++: Est-ce une mauvaise pratique de créer des blocs de code? et Code de structure avec accolades . Peut-être que l'un de ceux-ci contient une réponse à cette question, bien que C ++ et Java soient très différents à cet égard.
amon
4
@gnat Pourquoi cette question est-elle même ouverte? C'est l'une des choses les plus vastes que j'ai jamais vues. Est-ce une tentative d'une question canonique?
jpmc26

Réponses:

34

Premièrement, en parlant aux mécanismes sous-jacents:

En portée C ++ == durée de vie b / c, les destructeurs sont appelés à la sortie de la portée. De plus, une distinction importante en C / C ++ permet de déclarer des objets locaux. Dans l’exécution de C / C ++, le compilateur allouera à l’avance un cadre de pile pour la méthode aussi grande que nécessaire, plutôt que d’allouer plus d’espace de pile en entrée à chaque étendue (qui déclare des variables). Le compilateur est donc en train de réduire ou d’aplatir les portées.

Le compilateur C / C ++ peut réutiliser l’espace de stockage de la pile pour les locaux n’entrant pas en conflit avec la durée de vie (il utilisera généralement l’analyse du code réel pour déterminer cela plutôt que la portée, car c’est plus précis que la portée!).

Je parle de C / C ++ b / c. La syntaxe de Java, c’est-à-dire les accolades et la portée, est au moins en partie dérivée de cette famille. Et aussi parce que C ++ est apparu dans les commentaires de la question.

En revanche, il n’est pas possible d’avoir des objets locaux en Java: tous les objets sont des objets tas, et tous les locals / formals sont des variables de référence ou des types primitifs (d’ailleurs, il en va de même pour la statique).

En outre, en Java, la portée et la durée de vie ne sont pas exactement assimilées: être dans / hors de la portée est essentiellement un concept de compilation qui concerne les conflits d’accessibilité et de nom; rien ne se passe vraiment dans Java à la sortie de la portée en ce qui concerne le nettoyage des variables. La récupération de place de Java détermine (le point final de la) durée de vie des objets.

De plus, le mécanisme de bytecode de Java (la sortie du compilateur Java) tend à promouvoir les variables utilisateur déclarées dans des portées limitées au niveau supérieur de la méthode, car il n’existe pas de fonctionnalité de portée au niveau du bytecode (ce traitement est similaire au traitement C / C ++ du empiler). Au mieux, le compilateur pourrait réutiliser des emplacements de variable locaux, mais (contrairement à C / C ++) uniquement si leur type est identique.

(Cependant, pour être sûr que le compilateur JIT sous-jacent au moment de l'exécution puisse réutiliser le même espace de stockage de pile pour deux variables locales typées différemment (et différentes avec une fente), avec une analyse suffisante du bytecode.)

À l’avantage du programmeur, j’aurais tendance à convenir avec d’autres qu’une méthode (privée) est une meilleure construction, même si elle n’est utilisée qu’une fois.

Pourtant, il n'y a rien de mal à l'utiliser pour résoudre des noms de conflit.

Je l'ai fait dans de rares circonstances lorsque la fabrication de méthodes individuelles est un fardeau. Par exemple, lorsque vous écrivez un interpréteur en utilisant une grande switchinstruction dans une boucle, je pourrais (en fonction des facteurs) introduire un bloc distinct pour chaque cas caseafin que les cas individuels soient plus séparés les uns des autres, au lieu de faire de chaque cas une méthode différente (chaque méthode étant différente). qui n’est invoqué qu’une fois).

(Notez qu'en tant que code par blocs, les cas individuels ont accès aux instructions "break;" et "continue;" concernant la boucle englobante, alors que les méthodes nécessiteraient le renvoi de booléens et l'utilisation de conditionnels par l'appelant pour accéder à ces flux de contrôle. déclarations.)

Erik Eidt
la source
Réponse très intéressante (+1)! Cependant, un complément à la partie C ++. Si mot de passe est une chaîne dans un bloc, l'objet chaîne allouera de l'espace quelque part dans le magasin libre à la partie de taille variable. En quittant le bloc, la chaîne sera détruite, entraînant la désallocation de la partie variable. Mais comme ce n'est pas sur la pile, rien ne garantit qu'il sera bientôt écrasé. Alors mieux gérer la confidentialité en écrasant chacun de ses caractères avant que la chaîne ne soit détruite ;-)
Christophe
1
@ErikEidt J'essayais d'attirer l'attention sur le problème de parler de "C / C ++" comme s'il s'agissait en réalité d'une "chose", mais ce n'est pas le cas: "Au moment de l'exécution de C / C ++, le compilateur allouera généralement un cadre de pile pour la méthode aussi large que nécessaire », mais C n’a pas de méthode du tout. Il a des fonctions. Celles-ci sont différentes, notamment en C ++.
tchrist
7
" En revanche, il n’est pas possible d’avoir des objets locaux en Java: tous les objets sont des objets tas, et tous les locals / formals sont des variables de référence ou des types primitifs (au fait, il en va de même pour la statique). " pas tout à fait vrai, surtout pas pour les variables locales de la méthode. L'analyse d'échappement peut affecter des objets à la pile.
Boris l'Araignée
1
« Au mieux, le compilateur pourrait réutiliser des emplacements de variable locaux, mais (contrairement à C / C ++) uniquement si leur type est identique. ”Est ​​tout simplement faux. Au niveau du bytecode, les variables locales peuvent être assignées et réaffectées avec ce que vous voulez, la seule contrainte est que l' utilisation ultérieure soit compatible avec le type du dernier élément attribué. La réutilisation des emplacements de variables locales est la norme. Par exemple, avec { int x = 42; String s="blah"; } { long y = 0; }, la longvariable réutilisera les emplacements des deux variables xet savec tous les compilateurs couramment utilisés ( javacet ecj).
Holger
1
@ Boris the Spider: c'est une déclaration familière souvent répétée qui ne correspond pas vraiment à ce qui se passe. L'analyse d'évasion peut permettre la scalarisation, qui consiste à décomposer les champs de l'objet en variables, qui sont ensuite soumises à des optimisations supplémentaires. Certains d'entre eux peuvent être éliminés s'ils ne sont pas utilisés, d'autres peuvent être pliés avec les variables source utilisées pour les initialiser, d'autres sont mappés aux registres de la CPU. Le résultat correspond rarement à ce que les gens pourraient imaginer en disant «alloué sur la pile».
Holger
27

À mon avis, il serait plus clair de tirer le bloc de sa propre méthode. Si vous laissez ce bloc à l'intérieur, j'espère voir un commentaire expliquant pourquoi vous le placez là. À ce stade, il est tout simplement plus propre d’avoir l’appel de méthode initializeKeyManager()qui garde la variable de mot de passe limitée à la portée de la méthode et indique votre intention avec le bloc de code.

Casey Langford
la source
2
Je pense que votre réponse passe à côté d'un cas d'utilisation clé, c'est une étape intermédiaire lors d'un refactor. Il peut être utile de limiter la portée et de savoir que votre code fonctionne toujours juste avant l'extraction d'une méthode.
RubberDuck
16
@RubberDuck c'est vrai, mais dans ce cas, le reste d'entre nous ne devrait jamais le voir, donc ce que nous pensons n'a pas d'importance. Ce à quoi un codeur adulte et un compilateur consentant se mêlent dans l'intimité de leur propre compartiment ne concerne plus personne.
candied_orange
@candied_orange Je crains de ne pas l'obtenir :( Vous souhaitez élaborer?
doubleOrt
@doubleOrt Je voulais dire que les étapes intermédiaires de refactoring sont réelles et nécessaires, mais n'ont certainement pas besoin d'être inscrites dans le contrôle de source où tout le monde peut les consulter. Il suffit de terminer le refactoring avant votre arrivée.
candied_orange
@candied_orange Oh, je pensais que vous discutiez et que vous n'étendiez pas l'argument de RubberDuck.
DoubleOrt
17

Ils peuvent être utiles à Rust, avec ses règles d'emprunt strictes. Et généralement, dans les langages fonctionnels, où ce bloc renvoie une valeur. Mais dans des langages comme Java? Bien que l'idée semble élégante, elle est rarement utilisée dans la pratique, aussi la confusion de cette vision réduira-t-elle la clarté potentielle de la limitation du champ d'application des variables.

Il existe un cas où je trouve cela utile - dans une switchdéclaration! Parfois, vous voulez déclarer une variable dans un case:

switch (op) {
    case ADD:
        int result = a + b;
        System.out.printf("%s + %s = %s\n", a, b, result);
        break;
    case SUB:
        int result = a - b;
        System.out.printf("%s - %s = %s\n", a, b, result);
        break;
}

Ceci, cependant, échouera:

error: variable result is already defined in method main(String[])
            int result = a - b;

switch les déclarations sont étranges - ce sont des déclarations de contrôle, mais en raison de leur chute, elles n'introduisent pas d'étendue.

Donc, vous pouvez simplement utiliser des blocs pour créer les étendues vous-même:

switch (op) {
    case ADD: {
        int result = a + b;
        System.out.printf("%s + %s = %s\n", a, b, result);
    } break;
    case SUB: {
        int result = a - b;
        System.out.printf("%s - %s = %s\n", a, b, result);
    } break;
}
Idan Arye
la source
6
  • Cas général:

Existe-t-il un cas où l’utilisation de blocs uniquement pour réduire la portée de la variable a du sens?

Il est généralement considéré comme une bonne pratique de garder les méthodes courtes. Réduire la portée de certaines variables peut être un signe que votre méthode est assez longue, suffisamment pour que vous pensiez que la portée des variables doit être réduite. Dans ce cas, il vaut probablement la peine de créer une méthode distincte pour cette partie du code.

Les cas qui me posent problème sont ceux où cette partie du code utilise plus d’une poignée d’arguments. Il existe des situations où vous pouvez vous retrouver avec de petites méthodes qui prennent chacune beaucoup de paramètres (ou qui nécessitent une solution de contournement pour simuler le renvoi de plusieurs valeurs). La solution à ce problème particulier consiste à créer une autre classe qui conserve les diverses variables que vous utilisez et implémente. Elle implémente toutes les étapes que vous avez à l'esprit dans ses méthodes relativement courtes: il s'agit d'un cas d'utilisation typique de l'utilisation d'un modèle Builder .

Cela dit, toutes ces directives de codage peuvent parfois être appliquées de manière approximative. Il arrive parfois que le code soit plus lisible si vous le conservez dans une méthode légèrement plus longue, au lieu de le diviser en petites sous-méthodes (ou modèles de constructeur). En règle générale, cela fonctionne pour les problèmes de taille moyenne, plutôt linéaires et où trop de fractionnements nuisent à la lisibilité (surtout si vous devez basculer entre trop de méthodes pour comprendre ce que fait le code).

Cela peut avoir un sens, mais c'est très rare.

  • Votre cas d'utilisation:

Voici votre code:

KeyManagerFactory keyManager = KeyManagerFactory.getInstance("SunX509");
Keystore keyStore = KeyStore.getInstance("JKS");

{
    char[] password = getPassword();
    keyStore.load(new FileInputStream(keyStoreLocation), password);
    keyManager.init(keyStore, password);
}

Vous créez un FileInputStream, mais vous ne le fermez jamais.

Une façon de résoudre ce problème consiste à utiliser une instruction try-with-resources . Il étend la InputStream, et vous pouvez également utiliser ce bloc pour réduire la portée d'autres variables si vous le souhaitez (dans des limites raisonnables, puisque ces lignes sont liées):

KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Keystore keyStore = KeyStore.getInstance("JKS");

try (InputStream fis = new FileInputStream(keyStoreLocation)) {
    char[] password = getPassword();
    keyStore.load(fis, password);
    keyManager.init(keyStore, password);
}

(C'est aussi souvent préférable d'utiliser getDefaultAlgorithm()au lieu de SunX509, d'ailleurs.)

En outre, il est généralement judicieux de séparer des fragments de code en méthodes distinctes. Cela vaut rarement la peine si c'est juste pour 3 lignes (c'est le cas où la création d'une méthode séparée gêne la lisibilité).

Bruno
la source
"La réduction de la portée de certaines variables peut être un signe que votre méthode est assez longue" - Cela peut être un signe, mais il le peut aussi et IMHO devrait être utilisé afin de documenter la durée d'utilisation de la valeur d'une variable locale. En fait, pour les langues ne prenant pas en charge les portées imbriquées (par exemple, Python), j'apprécierais que le commentaire de code correspondant indique la "fin de vie" d'une variable. Si la même variable devait être réutilisée plus tard, il serait facile de déterminer si elle doit (ou devrait) être initialisée avec une nouvelle valeur quelque part avant (mais après le commentaire "fin de vie").
Jonny Dee
2

À mon humble avis, il s'agit généralement d'une solution à éviter en grande partie pour le code de production car vous n'êtes généralement pas tenté de le faire, sauf dans les fonctions sporadiques qui effectuent des tâches disparates. J'ai tendance à le faire dans certains codes de scrap utilisés pour tester des choses, mais je ne trouve aucune tentation de le faire dans un code de production où j'ai réfléchi à l'avance à ce que chaque fonction devrait faire, car elle aura naturellement une très grande portée limitée par rapport à son état local.

Je n'ai jamais vraiment vu d'exemples de blocs anonymes utilisés de cette façon (sans condition, sans tryblocage de transaction, etc.) pour réduire le champ d'application de manière significative dans une fonction qui ne posait pas la question de savoir pourquoi cela ne pouvait pas. être divisée plus loin en fonctions plus simples à portée réduite si elle bénéficiait réellement d’un véritable point de vue SE des blocs anonymes. Il s’agit généralement d’un code éclectique qui utilise un tas de choses vaguement ou très peu liées pour lesquelles nous sommes le plus tentés d’atteindre cet objectif.

Par exemple, si vous essayez de faire cela pour réutiliser une variable nommée count, cela suggère que vous comptez deux choses disparates. Si le nom de la variable doit être aussi court que possible count, il me semble logique de le lier au contexte de la fonction, ce qui pourrait éventuellement ne compter que pour un type de chose. Vous pouvez ensuite consulter instantanément le nom de la fonction et / ou sa documentation, et voir countimmédiatement ce que cela signifie dans le contexte de ce que fait la fonction sans analyser tout le code. Je ne trouve pas souvent le bon argument pour qu'une fonction compte deux choses différentes réutilisant le même nom de variable de manière à rendre les étendues / blocs anonymes si attrayants par rapport aux alternatives. Cela ne veut pas dire que toutes les fonctions ne doivent compter qu’une seule chose. JE'L’avantage technique d’une fonction réutilisant le même nom de variable pour compter deux choses ou plus et utilisant des blocs anonymes pour limiter la portée de chaque compte individuel. Si la fonction est simple et claire, ce n’est pas la fin du monde d’avoir deux variables de dénomination nommées différemment, la première pouvant avoir un nombre de lignes de visibilité de l’application supérieur à ce qui est idéalement requis. De telles fonctions ne sont généralement pas la source d'erreurs manquant de tels blocs anonymes pour réduire encore la portée minimale de ses variables locales.

Pas une suggestion pour les méthodes superflues

Cela ne veut pas dire que vous créez de force des méthodes ou simplement pour réduire la portée. C'est sans doute tout aussi mauvais ou pire, et ce que je suggère, ne devrait pas faire davantage appel à un besoin de méthodes "d'assistance" privées maladroites qu'à un besoin de portées anonymes. C'est trop penser au code actuel et à la façon de réduire la portée des variables que de penser à la façon de résoudre le problème au niveau de l'interface de manière conceptuelle, de manière à obtenir une visibilité nette et courte des états de fonction locaux naturellement, sans imbrication profonde de blocs et plus de 6 niveaux d'indentation. Je suis d'accord avec Bruno sur le fait que vous pouvez entraver la lisibilité du code en insérant avec force 3 lignes de code dans une fonction, mais cela part du principe que vous faites évoluer les fonctions que vous créez en fonction de votre implémentation existante. plutôt que de concevoir les fonctions sans s'embrouiller dans les implémentations. Si vous le faites de cette manière, j'ai trouvé peu de besoin de blocs anonymes qui ne servent à rien, sauf à réduire la portée d'une variable dans une méthode donnée, sauf si vous essayez avec zèle de réduire la portée d'une variable de quelques lignes de code inoffensif où l'introduction exotique de ces blocs anonymes contribue sans doute autant à la surcharge intellectuelle qu'à la suppression.

Essayer de réduire encore les portées minimales

Si réduire la portée des variables locales au minimum absolu en valait la peine, le code devrait être largement accepté, comme ceci:

ImageIO.write(new Robot("borg").createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())), "png", new File(Db.getUserId(User.handle()).toString()));

... car cela entraîne une visibilité minimale de l'état en ne créant même pas de variables pour s'y référer en premier lieu. Je ne veux pas devenir dogmatique, mais je pense en fait que la solution pragmatique consiste à éviter les blocs anonymes autant que possible, tout comme à éviter la monstrueuse ligne de code ci-dessus, et si cela semble absolument nécessaire dans un contexte de production perspective de correction et de maintien des invariants dans une fonction, alors je pense vraiment que la façon dont vous organisez votre code en fonctions et la conception de vos interfaces mérite un nouvel examen. Naturellement, si votre méthode comporte 400 lignes et que la portée d'une variable est visible pour 300 lignes de code de plus que nécessaire, cela peut constituer un véritable problème d'ingénierie, mais ce n'est pas nécessairement un problème à résoudre avec des blocs anonymes.

Utiliser des blocs anonymes partout est exotique, pas idiomatique, et un code exotique risque d'être détesté par d'autres, si ce n'est vous-même, des années plus tard.

L'utilité pratique de la réduction de la portée

L’utilité ultime de la réduction du nombre de variables est de vous permettre d’obtenir une gestion de l’état correcte, de la conserver et de vous permettre de raisonner facilement sur ce que fait une partie donnée d’une base de code - pour pouvoir conserver des invariants conceptuels. Si la gestion de l'état local par une seule fonction est si complexe que vous devez réduire de manière décisive la portée avec un bloc anonyme dans du code qui n'est pas censé être finalisé et bon, là encore, c'est un signe pour moi que la fonction elle-même doit être réexaminée. . Si vous avez des difficultés à raisonner sur la gestion par l'état des variables dans une étendue de fonction locale, imaginez la difficulté de raisonner sur les variables privées accessibles à toutes les méthodes d'une classe entière. Nous ne pouvons pas utiliser de blocs anonymes pour réduire leur visibilité. Pour moi, il est utile de commencer par accepter que les variables tendent à avoir une portée légèrement plus large que celle dont elles auraient idéalement besoin dans de nombreuses langues, à condition que vous ne perdiez pas le contrôle de la situation au point que vous aurez du mal à maintenir les invariants. Ce n'est pas quelque chose à résoudre avec des blocs anonymes, je le vois comme accepter d'un point de vue pragmatique.


la source
Comme mise en garde finale, comme je l’ai déjà mentionné, j’utilise toujours des blocs anonymes de temps en temps, souvent dans les méthodes de point d’entrée principale pour le code de rebut. Quelqu'un d'autre l'a mentionné comme un outil de refactoring et je pense que ça va, car le résultat est intermédiaire et destiné à être refactorisé davantage, pas de code finalisé. Je ne néglige pas totalement leur utilité, mais si vous essayez d'écrire un code largement accepté, bien testé et correct, je ne vois guère la nécessité de blocs anonymes. Devenir obsédé par leur utilisation peut détourner l'attention de la conception de fonctions plus claires avec des blocs naturellement petits.
2

Existe-t-il un cas où l’utilisation de blocs uniquement pour réduire la portée de la variable a du sens?

Je pense que cette motivation est une raison suffisante pour introduire un bloc, oui.

Comme Casey l'a noté, le bloc est une "odeur de code" qui indique qu'il est peut-être préférable d'extraire le bloc dans une fonction. Mais vous ne pouvez pas.

John Carmark a rédigé une note sur le code incorporé en 2007. Son résumé final

  • Si une fonction n'est appelée qu'à partir d'un seul endroit, envisagez de la mettre en ligne.
  • Si une fonction est appelée à partir de plusieurs emplacements, voyez s'il est possible de prendre des dispositions pour que le travail soit effectué à un seul endroit, éventuellement avec des drapeaux.
  • S'il existe plusieurs versions d'une fonction, envisagez de créer une seule fonction avec plusieurs paramètres, éventuellement par défaut.
  • Si le travail est presque purement fonctionnel, avec peu de références à l'état global, essayez de le rendre complètement fonctionnel.

Alors réfléchissez , faites un choix avec un but précis et laissez (facultatif) une preuve décrivant votre motivation pour le choix que vous avez fait.

VoiceOfUnreason
la source
1

Mon opinion peut être controversée, mais oui, je pense qu'il y a certainement des situations où j'utiliserais ce type de style. Cependant, "simplement pour réduire la portée de la variable" n'est pas un argument valable, car c'est ce que vous faites déjà. Et faire quelque chose juste pour le faire n'est pas une raison valable. Notez également que cette explication n'a aucune signification si votre équipe a déjà résolu si ce type de syntaxe est conventionnelle ou non.

Je suis principalement un programmeur C #, mais j’ai entendu dire qu’en Java, le renvoi d’un mot de passe tel char[]quel vous permet de réduire le temps pendant lequel le mot de passe restera en mémoire. Dans cet exemple, cela ne vous aidera pas, car le ramasse-miettes est autorisé à collecter le tableau à partir du moment où il n'est pas utilisé. Par conséquent, le fait que le mot de passe quitte la portée importe peu. Je ne dis pas s'il est viable d'effacer le tableau une fois que vous en avez terminé, mais dans ce cas, si vous souhaitez le faire, il est logique d'étudier la variable:

{
    char[] password = getPassword();
    try{
        keyStore.load(new FileInputStream(keyStoreLocation), password);
        keyManager.init(keyStore, password);
    }finally{
        Arrays.fill(password, '\0');
    }
}

Ceci est très similaire à l' instruction try-with-resources , car elle étend la ressource et effectue une finalisation dessus une fois que vous avez terminé. Veuillez noter à nouveau que je ne plaide pas pour la gestion des mots de passe de cette manière, mais que si vous décidez de le faire, ce style est raisonnable.

La raison en est que la variable n'est plus valide. Vous l'avez créé, utilisé et invalidé son état pour ne contenir aucune information significative. Cela n'a aucun sens d'utiliser la variable après ce bloc, il est donc raisonnable de l'étendre.

Un autre exemple auquel je peux penser est lorsque vous avez deux variables qui ont un nom et une signification similaires, mais que vous travaillez avec une, puis une autre, et que vous voulez les garder séparées. J'ai écrit ce code en C #:

{
    MethodBuilder m_ToString = tb.DefineMethod("ToString", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, typeofString, Type.EmptyTypes);
    var il = m_ToString.GetILGenerator();
    il.Emit(OpCodes.Ldstr, templateType.ToString()+":"+staticType.ToString());
    il.Emit(OpCodes.Ret);
    tb.DefineMethodOverride(m_ToString, m_Object_ToString);
}
{
    PropertyBuilder p_Class = tb.DefineProperty("Class", PropertyAttributes.None, typeofType, Type.EmptyTypes);
    MethodBuilder m_get_Class = tb.DefineMethod("get_Class", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, typeofType, Type.EmptyTypes);
    var il = m_get_Class.GetILGenerator();
    il.Emit(OpCodes.Ldtoken, staticType);
    il.Emit(OpCodes.Call, m_Type_GetTypeFromHandle);
    il.Emit(OpCodes.Ret);
    p_Class.SetGetMethod(m_get_Class);
    tb.DefineMethodOverride(m_get_Class, m_IPattern_get_Class);
}

Vous pouvez faire valoir que je peux simplement déclarer ILGenerator il;en haut de la méthode, mais je ne veux pas non plus réutiliser la variable pour différents objets (approche un peu fonctionnelle). Dans ce cas, les blocs facilitent la séparation des tâches effectuées, à la fois de manière syntaxique et visuelle. Il indique également qu'après le blocage, j'en ai fini ilet rien ne devrait y accéder.

Un argument contre cet exemple utilise des méthodes. Peut-être que oui, mais dans ce cas, le code n'est pas si long, et le séparer en différentes méthodes nécessiterait également de transmettre toutes les variables utilisées dans le code.

IllidanS4 veut récupérer Monica
la source