Ha senso creare blocchi solo per ridurre l'ambito di una variabile?


38

Sto scrivendo un programma in Java dove ad un certo punto devo caricare una password per il mio keystore. Solo per divertimento, ho provato a mantenere la mia password in Java il più breve possibile facendo questo:

//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

Ora, so in questo caso che è un po 'stupido. Ci sono un sacco di altre cose che avrei potuto fare, la maggior parte in realtà migliore (non avrei potuto usare una variabile).

Tuttavia, ero curioso di sapere se c'è un caso in cui farlo non è stato così stupido. L'unica altra cosa a cui riesco a pensare è se volessi riutilizzare nomi di variabili comuni come count, o temp, ma buone convenzioni di denominazione e metodi brevi rendono improbabile che sarebbe utile.

C'è un caso in cui ha senso usare i blocchi solo per ridurre l'ambito variabile?


13
@gnat .... cosa? Non so nemmeno come modificare la mia domanda in modo che sia meno simile a quel target duplicato. Quale parte di questo ti ha portato a credere che queste domande siano le stesse?
Lord Farquaad,

22
La solita risposta a questo è che il tuo blocco anonimo dovrebbe essere un metodo con un nome significativo, che ti dà la documentazione automatica e l'ambito ridotto che volevi. Davvero, non riesco a pensare a una situazione in cui l'estrazione di un metodo non è meglio dell'inserimento di un blocco nudo.
Kilian Foth,

4
Ci sono un paio di domande simili che chiedono questo per C ++: è una cattiva pratica creare blocchi di codice? e codice struttura con parentesi graffe . Forse uno di questi contiene una risposta a questa domanda, sebbene C ++ e Java siano molto diversi da questo punto di vista.
amon


4
@gnat Perché questa domanda è ancora aperta? È una delle cose più ampie che abbia mai visto. È un tentativo di una domanda canonica?
jpmc26,

Risposte:


34

Innanzitutto, parlando con la meccanica sottostante:

In C ++ scope == i distruttori b / c di durata vengono richiamati all'uscita dall'ambito. Inoltre, un'importante distinzione in C / C ++ possiamo dichiarare oggetti locali. Nel runtime per C / C ++ il compilatore alloca generalmente un frame di stack per il metodo che è grande quanto potrebbe essere necessario, in anticipo, piuttosto che allocare più spazio di stack all'ingresso per ciascun ambito (che dichiara le variabili). Quindi, il compilatore sta collassando o appiattendo gli ambiti.

Il compilatore C / C ++ può riutilizzare lo spazio di archiviazione dello stack per i locali che non sono in conflitto nella durata di utilizzo (generalmente utilizzerà l'analisi del codice effettivo per determinare questo anziché l'ambito, b / c che è più preciso dell'ambito!).

Cito C / C ++ b / c La sintassi di Java, ovvero parentesi graffe e scoping, è almeno in parte derivata da quella famiglia. E anche perché il C ++ è arrivato nei commenti alle domande.

Al contrario, non è possibile avere oggetti locali in Java: tutti gli oggetti sono oggetti heap e tutti i locali / formali sono variabili di riferimento o tipi primitivi (a proposito, lo stesso vale per la statica).

Inoltre, in ambito Java e durata non sono esattamente equiparati: essere dentro / fuori dall'ambito è un concetto per lo più di tempo di compilazione che va all'accessibilità e ai conflitti di nomi; nulla accade realmente in Java all'uscita dell'ambito per quanto riguarda la pulizia delle variabili. La garbage collection di Java determina (il punto finale della) durata degli oggetti.

Anche il meccanismo bytecode di Java (l'output del compilatore Java) tende a promuovere le variabili utente dichiarate in ambiti limitati al livello superiore del metodo, poiché non esiste alcuna funzionalità di ambito a livello di bytecode (è simile alla gestione C / C ++ del pila). Nella migliore delle ipotesi il compilatore potrebbe riutilizzare gli slot delle variabili locali, ma (a differenza di C / C ++) solo se il loro tipo è lo stesso.

(Tuttavia, per essere sicuri che il compilatore JIT sottostante nel runtime possa riutilizzare lo stesso spazio di archiviazione dello stack per due locali locali di tipo diverso (e con slot diversi), con un'analisi sufficiente del bytecode.)

Parlando a vantaggio del programmatore, tenderei ad essere d'accordo con gli altri sul fatto che un metodo (privato) è un costrutto migliore anche se usato solo una volta.

Tuttavia, non c'è nulla di veramente sbagliato nell'usarlo per deconflivere i nomi.

L'ho fatto in rare circostanze quando fare dei metodi individuali è un peso. Ad esempio, quando scrivo un interprete usando una switchdichiarazione di grandi dimensioni all'interno di un ciclo, potrei (a seconda dei fattori) introdurre un blocco separato per ciascuno caseper mantenere i singoli casi più separati l'uno dall'altro, invece di rendere ogni caso un metodo diverso (ciascuno di che viene invocato una sola volta).

(Si noti che come codice in blocchi, i singoli casi hanno accesso alle istruzioni "break;" e "continue;" per quanto riguarda il ciclo che racchiude, mentre poiché i metodi richiederebbero il ritorno di booleani e l'uso di condizionali da parte del chiamante per accedere a questi flussi di controllo dichiarazioni.)


Risposta molto interessante (+1)! Comunque un complemento alla parte C ++. Se la password è una stringa in un blocco, l'oggetto stringa allocerà lo spazio da qualche parte nell'archivio gratuito per la parte di dimensioni variabili. Quando si lascia il blocco, la stringa verrà distrutta, determinando la deallocazione della parte variabile. Ma poiché non è in pila, nulla garantisce che verrà sovrascritto presto. Quindi gestisci meglio la riservatezza sovrascrivendo ciascuno dei suoi personaggi prima che la stringa venga distrutta ;-)
Christophe,

1
@ErikEidt Stavo cercando di attirare l'attenzione sul problema di parlare di "C / C ++" come se fosse effettivamente una "cosa", ma non lo è: "Nel runtime per C / C ++ il compilatore generalmente alloca un frame di stack per il metodo più grande che potrebbe essere necessario " ma C non ha affatto metodi. Ha funzioni. Questi sono diversi, specialmente in C ++.
tchrist,

7
" Al contrario, non è possibile avere oggetti locali in Java: tutti gli oggetti sono oggetti heap e tutti i locali / formali sono variabili di riferimento o tipi primitivi (a proposito, lo stesso vale per la statica). " - Nota che questo è non del tutto vero, soprattutto non per le variabili locali del metodo. L'analisi di escape può allocare oggetti allo stack.
Boris the Spider il

1
Nella migliore delle ipotesi il compilatore potrebbe riutilizzare gli slot delle variabili locali, ma (a differenza di C / C ++) solo se il loro tipo è lo stesso. "È semplicemente sbagliato. A livello di bytecode, le variabili locali possono essere assegnate e riassegnate con qualsiasi cosa tu voglia, l'unico vincolo è che l' uso successivo è compatibile con l'ultimo tipo di elemento assegnato. Il riutilizzo degli slot delle variabili locali è la norma, ad esempio con { int x = 42; String s="blah"; } { long y = 0; }, la longvariabile riutilizzerà gli slot di entrambe le variabili xe scon tutti i compilatori ( javace ecj) comunemente utilizzati .
Holger,

1
@Boris the Spider: è un'affermazione colloquiale spesso ripetuta che non corrisponde davvero a quello che sta succedendo. Escape Analysis può abilitare la scalarizzazione, che è la decomposizione dei campi dell'oggetto in variabili, che sono quindi soggette a ulteriori ottimizzazioni. Alcuni di essi potrebbero essere eliminati come inutilizzati, altri potrebbero essere piegati con le variabili di origine utilizzate per inizializzarli, altri potrebbero essere associati ai registri della CPU. Il risultato raramente corrisponde a ciò che la gente potrebbe immaginare quando dice "allocato in pila".
Holger,

27

Secondo me sarebbe più chiaro estrarre il blocco nel suo metodo. Se hai lasciato questo blocco, spero di vedere un commento che chiarisca perché stai mettendo il blocco lì in primo luogo. A quel punto è più semplice avere la chiamata del metodo a initializeKeyManager()cui la variabile password è limitata all'ambito del metodo e mostra le tue intenzioni con il blocco di codice.


2
Penso che la tua risposta sgorghi da un caso chiave, è un passaggio intermedio durante un refactor. Può essere utile limitare l'ambito e sapere che il codice funziona ancora prima dell'estrazione di un metodo.
RubberDuck,

16
@RubberDuck vero, ma in quel caso il resto di noi non dovrebbe mai vederlo, quindi non importa cosa pensiamo. Ciò che un programmatore adulto e un compilatore consenziente riesce a ottenere nella privacy del proprio cubicolo non è preoccupazione di nessun altro.
candied_orange,

@candied_orange Temo di non averlo capito :( Cura di elaborare?
doubleOrt

@doubleOrt Volevo dire che i passaggi intermedi del refactoring sono reali e necessari, ma certamente non devono essere racchiusi nel controllo del codice sorgente in cui tutti possono guardarlo. Finisci il refactoring prima del check-in.
candied_orange

@candied_orange Oh, pensavo che stessi discutendo e non estendendo l'argomento di RubberDuck.
doubleOrt

17

Possono essere utili in Rust, con le sue rigide regole di prestito. E generalmente, in linguaggi funzionali, in cui quel blocco restituisce un valore. Ma in lingue come Java? Sebbene l'idea sembri elegante, in pratica viene usata raramente, quindi la confusione derivante dal vederla ridurrà la potenziale chiarezza nel limitare la portata delle variabili.

C'è un caso in cui lo trovo utile - in una switchdichiarazione! A volte vuoi dichiarare una variabile in 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;
}

Questo, tuttavia, fallirà:

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

switch le istruzioni sono strane - sono dichiarazioni di controllo, ma a causa della loro caduta non introducono ambiti.

Quindi, puoi semplicemente usare i blocchi per creare gli ambiti da solo:

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;
}

6
  • Caso generale:

C'è un caso in cui ha senso usare i blocchi solo per ridurre l'ambito variabile?

È generalmente considerata una buona pratica mantenere i metodi brevi. Ridurre l'ambito di alcune variabili può essere un segno che il tuo metodo è piuttosto lungo, sufficientemente da pensare che l'ambito delle variabili debba essere ridotto. In questo caso, probabilmente vale la pena creare un metodo separato per quella parte del codice.

I casi che troverei problematici sono quando quella parte di codice utilizza più di una manciata di argomenti. Ci sono situazioni in cui potresti finire con piccoli metodi che accettano molti parametri (o che richiedono una soluzione alternativa per simulare la restituzione di più valori). La soluzione a quel particolare problema è quella di creare un'altra classe che mantenga le varie variabili che usi e implementa implementa tutti i passaggi che hai in mente nei suoi metodi relativamente brevi: questo è un tipico caso d'uso per usare un modello Builder .

Detto questo, a volte tutte queste linee guida per la codifica possono essere applicate liberamente. Ci sono casi strani a volte, in cui il codice può essere più leggibile se lo tieni in un metodo leggermente più lungo, invece di dividerlo in piccoli sotto-metodi (o modelli di builder). In genere, questo funziona per problemi di medie dimensioni, abbastanza lineari e dove troppa suddivisione in realtà ostacola la leggibilità (soprattutto se è necessario saltare tra troppi metodi per capire cosa fa il codice).

Può avere senso, ma è molto raro.

  • Il tuo caso d'uso:

Ecco il tuo codice:

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

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

Stai creando un FileInputStream, ma non lo chiudi mai.

Un modo per risolverlo è utilizzare un'istruzione try-with-resources . Contiene l'ambito InputStream, e puoi anche usare questo blocco per ridurre l'ambito di altre variabili se lo desideri (entro limiti ragionevoli, poiché queste linee sono correlate):

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);
}

(A proposito, è anche meglio usarlo getDefaultAlgorithm()invece SunX509, tra l'altro.)

Inoltre, mentre il consiglio di separare blocchi di codice in metodi separati è generalmente valido. Raramente ne vale la pena se è solo per 3 righe (semmai, è un caso in cui la creazione di un metodo separato ostacola la leggibilità).


"Ridurre l'ambito di alcune variabili può essere un segno che il tuo metodo è piuttosto lungo" - Può essere un segno ma può anche essere e IMHO dovrebbe essere usato per documentare per quanto tempo viene usato il valore di una variabile locale. In effetti, per le lingue che non supportano gli ambiti nidificati (ad esempio Python), apprezzerei se un commento di codice corrispondente affermasse la "fine della vita" di una variabile. Se la stessa variabile dovesse essere riutilizzata in un secondo momento, sarebbe facile capire se deve (o dovrebbe) essere stata inizializzata con un nuovo valore da qualche parte prima (ma dopo il commento di "fine vita").
Jonny Dee,

2

Secondo la mia modesta opinione, è generalmente qualcosa da evitare in gran parte per il codice di produzione perché in genere non si è tentati di farlo tranne che in sporadiche funzioni che svolgono compiti diversi. Tendo a farlo in qualche codice di scarto usato per testare le cose, ma non trovo alcuna tentazione di farlo nel codice di produzione dove ho pensato in anticipo a cosa dovrebbe fare ogni funzione, da allora la funzione avrà naturalmente un portata limitata rispetto al suo stato locale.

Non ho mai visto esempi di blocchi anonimi usati in questo modo (senza coinvolgere condizionali, un tryblocco per una transazione, ecc.) Per ridurre l'ambito in modo significativo in una funzione che non poneva la domanda sul perché non potesse essere ulteriormente suddiviso in funzioni più semplici con ambiti ridotti se effettivamente beneficiava praticamente di un vero punto di vista SE dai blocchi anonimi. Di solito è un codice eclettico che sta facendo un mucchio di cose vagamente correlate o molto non correlate in cui siamo più tentati di raggiungerlo.

Ad esempio, se stai provando a farlo per riutilizzare una variabile denominata count, ti suggerisce di contare due cose diverse. Se il nome della variabile sarà breve count, allora per me ha senso legarlo al contesto della funzione, che potrebbe potenzialmente contare solo su un tipo di cosa. Quindi puoi immediatamente guardare il nome e / o la documentazione della funzione, vedere counte sapere immediatamente cosa significa nel contesto di ciò che la funzione sta facendo senza analizzare tutto il codice. Non trovo spesso un buon argomento per una funzione per contare due cose diverse riutilizzando lo stesso nome di variabile in modi che rendono ambiti / blocchi anonimi così attraenti rispetto alle alternative. Ciò non suggerisce che tutte le funzioni debbano contare solo una cosa. IO'il vantaggio ingegneristico di una funzione che riutilizza lo stesso nome di variabile per contare due o più cose e utilizza blocchi anonimi per limitare l'ambito di ogni singolo conteggio. Se la funzione è semplice e chiara, non è la fine del mondo avere due variabili di conteggio con un nome diverso con la prima possibilmente con qualche linea in più di visibilità dell'ambito di quanto idealmente richiesto. Tali funzioni generalmente non sono la fonte di errori privi di tali blocchi anonimi per ridurre ulteriormente l'ambito minimo delle sue variabili locali.

Non è un suggerimento per i metodi superflui

Questo non per suggerire di creare con forza metodi né solo per ridurre l'ambito. Questo è probabilmente altrettanto brutto o peggio, e ciò che sto suggerendo non dovrebbe richiedere una necessità di metodi di "aiuto" privati ​​imbarazzanti più di una necessità di scopi anonimi. Questo sta pensando troppo al codice come è adesso e come ridurre l'ambito delle variabili piuttosto che pensare a come risolvere concettualmente il problema a livello di interfaccia in modo da produrre naturalmente una visibilità chiara e breve degli stati di funzione locale senza annidamento profondo dei blocchi e 6+ livelli di rientro. Concordo con Bruno sul fatto che puoi ostacolare la leggibilità del codice inserendo forzatamente 3 righe di codice in una funzione, ma questo a partire dal presupposto che stai evolvendo le funzioni che crei in base all'implementazione esistente, piuttosto che progettare le funzioni senza impigliarsi nelle implementazioni. Se lo fai in quest'ultimo modo, ho trovato poca necessità di blocchi anonimi che non servono a nulla oltre a ridurre l'ambito variabile all'interno di un determinato metodo a meno che tu non stia provando zelantemente a ridurre l'ambito di una variabile con solo poche righe di codice innocuo in cui l'introduzione esotica di questi blocchi anonimi contribuisce probabilmente tanto all'overhead intellettuale che rimuovono.

Cercare di ridurre ulteriormente gli scopi minimi

Se vale la pena ridurre al minimo assoluto gli ambiti delle variabili locali, dovrebbe esserci un'ampia accettazione di codice come questo:

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

... poiché ciò causa la minima visibilità dello stato, nemmeno creando variabili che possano fare riferimento a esse in primo luogo. Non voglio diventare dogmatico, ma in realtà penso che la soluzione pragmatica sia quella di evitare blocchi anonimi, quando possibile, così come lo è per evitare la mostruosa linea di codice sopra, e se sembrano così assolutamente necessari in un contesto di produzione dal prospettiva di correttezza e mantenimento degli invarianti all'interno di una funzione, quindi penso sicuramente a come stai organizzando il tuo codice in funzioni e progettando le tue interfacce. Naturalmente se il tuo metodo è lungo 400 righe e l'ambito di una variabile è visibile a 300 righe di codice in più del necessario, questo potrebbe essere un vero problema di ingegneria, ma non è necessariamente un problema da risolvere con blocchi anonimi.

Se non altro, l'utilizzo di blocchi anonimi in tutto il luogo è esotico, non idiomatico, e il codice esotico comporta il rischio di essere odiato dagli altri, se non da te stesso, anni dopo.

L'utilità pratica di ridurre la portata

L'utilità finale di ridurre l'ambito delle variabili è di permetterti di ottenere la gestione dello stato corretta e mantenerla corretta e permetterti di ragionare facilmente su ciò che fa una determinata porzione di una base di codice - per essere in grado di mantenere invarianti concettuali. Se la gestione dello stato locale di una singola funzione è così complessa che devi ridurre forzatamente l'ambito con un blocco anonimo nel codice che non è destinato a essere finalizzato e valido, quindi di nuovo, questo è un segno per me che la funzione stessa deve essere riesaminata . Se hai difficoltà a ragionare sulla gestione dello stato delle variabili nell'ambito di una funzione locale, immagina la difficoltà di ragionamento sulle variabili private accessibili a tutti i metodi di un'intera classe. Non possiamo usare blocchi anonimi per ridurne la visibilità. Per me aiuta iniziare con l'accettazione che le variabili tenderanno ad avere una portata leggermente più ampia di quella che idealmente devono avere in molte lingue, a condizione che non sfugga al punto in cui avrete difficoltà a mantenere gli invarianti. Non è qualcosa da risolvere così tanto con blocchi anonimi come lo vedo come accettazione da un punto di vista pragmatico.


Come ultimo avvertimento, come ho già detto, di tanto in tanto utilizzo ancora blocchi anonimi, spesso nei principali metodi di ingresso per il codice di scarto. Qualcun altro lo ha menzionato come uno strumento di refactoring e penso che vada bene, poiché il risultato è intermedio e destinato a essere ulteriormente refactored, non finalizzato al codice. Non sto scontando del tutto la loro utilità, ma se stai cercando di scrivere un codice ampiamente accettato, ben testato e corretto, vedo appena la necessità di blocchi anonimi. Essere ossessionati dal loro utilizzo può distogliere l'attenzione dalla progettazione di funzioni più chiare con blocchi naturalmente piccoli.

2

C'è un caso in cui ha senso usare i blocchi solo per ridurre l'ambito variabile?

Penso che la motivazione sia una ragione sufficiente per introdurre un blocco, sì.

Come notato da Casey, il blocco è un "odore di codice" che indica che è meglio estrarre il blocco in una funzione. Ma non puoi.

John Carmark ha scritto un promemoria sul codice incorporato nel 2007. Il suo riassunto conclusivo

  • Se una funzione viene chiamata solo da un singolo posto, considera di allinearla.
  • Se una funzione viene chiamata da più punti, verifica se è possibile organizzare il lavoro in un unico posto, magari con le bandiere, e incorporalo.
  • Se esistono più versioni di una funzione, prendere in considerazione l'idea di creare una singola funzione con più parametri, possibilmente predefiniti.
  • Se il lavoro è quasi puramente funzionale, con pochi riferimenti allo stato globale, prova a renderlo completamente funzionale.

Quindi pensa , fai una scelta con uno scopo e (facoltativo) lascia le prove che descrivono la tua motivazione per la scelta che hai fatto.


1

La mia opinione può essere controversa, ma sì, penso che ci siano certamente situazioni in cui userei questo tipo di stile. Tuttavia, "solo per ridurre l'ambito della variabile" non è un argomento valido, perché è quello che stai già facendo. E fare qualcosa solo per farlo non è un motivo valido. Nota anche che questa spiegazione non ha alcun significato se il tuo team ha già risolto se questo tipo di sintassi è convenzionale o meno.

Sono principalmente un programmatore C #, ma ho sentito che in Java, la restituzione di una password char[]è tale da consentire di ridurre il tempo in cui la password rimarrà in memoria. In questo esempio, non sarà di aiuto, poiché al garbage collector è consentito raccogliere l'array dal momento in cui non viene utilizzato, quindi non importa se la password lascia l'ambito. Non sto discutendo se sia possibile cancellare l'array dopo aver finito con esso, ma in questo caso, se si desidera farlo, l'ambito della variabile ha senso:

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

Questo è molto simile all'istruzione try-with-resources , perché scopre la risorsa ed esegue una finalizzazione su di essa al termine. Ancora una volta, tieni presente che non sto discutendo per la gestione delle password in questo modo, solo che se decidi di farlo, questo stile è ragionevole.

Il motivo è che la variabile non è più valida. L'hai creato, usato e invalidato il suo stato per non contenere informazioni significative. Non ha senso usare la variabile dopo questo blocco, quindi è ragionevole cercarla.

Un altro esempio a cui riesco a pensare è quando hai due variabili che hanno un nome e un significato simili, ma lavori con una e poi con un'altra e vuoi tenerle separate. Ho scritto questo codice in 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);
}

Puoi sostenere che posso semplicemente dichiarare ILGenerator il;all'inizio del metodo, ma non voglio nemmeno riutilizzare la variabile per oggetti diversi (un po 'approccio funzionale). In questo caso, i blocchi semplificano la separazione dei lavori eseguiti, sia sintatticamente che visivamente. Dice anche che dopo il blocco, ho finito ile niente dovrebbe accedervi.

Un argomento contro questo esempio sta usando i metodi. Forse sì, ma in questo caso, il codice non è così lungo e separarlo in metodi diversi dovrebbe anche passare attraverso tutte le variabili utilizzate nel codice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.