Considero questo un abuso della dichiarazione using. Sono consapevole di essere in minoranza in questa posizione.
Considero questo un abuso per tre ragioni.
Primo, perché mi aspetto che "using" sia utilizzato per utilizzare una risorsa e smaltirla quando hai finito . Cambiare lo stato del programma non sta usando una risorsa e cambiarlo non significa eliminare nulla. Pertanto, "usare" per mutare e ripristinare lo stato è un abuso; il codice è fuorviante per il lettore occasionale.
Secondo, perché mi aspetto che "usare" sia usato per cortesia, non per necessità . Il motivo per cui usi "using" per eliminare un file quando hai finito non è perché è necessario farlo, ma perché è educato - qualcun altro potrebbe essere in attesa di usare quel file, quindi dicendo "fatto adesso "è la cosa moralmente corretta da fare. Mi aspetto che dovrei essere in grado di effettuare il refactoring di un "utilizzo" in modo che la risorsa utilizzata venga trattenuta più a lungo e smaltita in un secondo momento, e che l'unico impatto di ciò sia di disturbare leggermente altri processi . Un blocco "using" che ha un impatto semantico sullo stato del programma è offensivo perché nasconde un'importante, richiesta mutazione dello stato del programma in un costrutto che sembra essere lì per comodità e cortesia, non per necessità.
E terzo, le azioni del tuo programma sono determinate dal suo stato; la necessità di un'attenta manipolazione dello stato è precisamente il motivo per cui stiamo avendo questa conversazione in primo luogo. Consideriamo come potremmo analizzare il tuo programma originale.
Se dovessi portarlo a una revisione del codice nel mio ufficio, la prima domanda che ti farei è "è davvero corretto bloccare il frobble se viene generata un'eccezione?" È palesemente ovvio dal tuo programma che questa cosa blocca nuovamente il frobble in modo aggressivo, indipendentemente da ciò che accade. È giusto? È stata generata un'eccezione. Il programma è in uno stato sconosciuto. Non sappiamo se Foo, Fiddle o Bar hanno lanciato, perché hanno lanciato o quali mutazioni hanno eseguito in altri stati che non sono stati ripuliti. Puoi convincermi che in quella terribile situazione è sempre la cosa giusta da fare richiudere?
Forse lo è, forse non lo è. Il punto è che con il codice come è stato scritto originariamente, il revisore del codice sa di porre la domanda . Con il codice che usa "using", non so fare la domanda; Presumo che il blocco "using" allochi una risorsa, la usi per un po 'e la elimini educatamente quando ha finito, non che la parentesi graffa di chiusura del blocco "using" muti lo stato del mio programma in una circostanza eccezionale quando arbitrariamente molti le condizioni di coerenza dello stato del programma sono state violate.
L'uso del blocco "using" per avere un effetto semantico rende questo programma frammentato:
}
estremamente significativo. Quando guardo quel tutore singolo vicino non penso immediatamente "quel tutore ha effetti collaterali che hanno impatti di vasta portata sullo stato globale del mio programma". Ma quando si abusa dell '"uso" in questo modo, improvvisamente lo fa.
La seconda cosa che ti chiederei se vedessi il tuo codice originale è "cosa succede se viene lanciata un'eccezione dopo lo sblocco ma prima che venga inserita la prova?" Se stai eseguendo un assembly non ottimizzato, il compilatore potrebbe aver inserito un'istruzione no-op prima del tentativo ed è possibile che si verifichi un'eccezione di interruzione del thread sul no-op. Questo è raro, ma accade nella vita reale, in particolare sui server web. In tal caso, lo sblocco avviene ma il blocco non avviene mai, perché l'eccezione è stata lanciata prima del tentativo. È del tutto possibile che questo codice sia vulnerabile a questo problema e debba essere effettivamente scritto
bool needsLock = false;
try
{
// must be carefully written so that needsLock is set
// if and only if the unlock happened:
this.Frobble.AtomicUnlock(ref needsLock);
blah blah blah
}
finally
{
if (needsLock) this.Frobble.Lock();
}
Di nuovo, forse sì, forse no, ma so di porre la domanda . Con la versione "using", è suscettibile allo stesso problema: un'eccezione di interruzione del thread potrebbe essere generata dopo che Frobble è stato bloccato ma prima che venga inserita la regione protetta da try associata all'uso. Ma con la versione "using", presumo che questo sia un "e allora?" situazione. È un peccato se ciò accade, ma presumo che l '"utilizzo" sia lì solo per essere educato, non per mutare lo stato del programma di vitale importanza. Presumo che se qualche terribile eccezione di interruzione del thread si verifica esattamente nel momento sbagliato, allora, vabbè, il garbage collector pulirà quella risorsa alla fine eseguendo il finalizzatore.