2015-04-14 8 views
13

Lo scopo dell'interfaccia IDisposable è di rilasciare le risorse non gestite in modo ordinato. Va di pari passo con la parola chiave using che definisce un ambito dopo il quale viene eliminata la risorsa in questione.L'uso improprio è suscettibile di trarre vantaggio dall'uso di dichiarazioni considerate dannose?

Poiché questo meachismo è così accurato, sono stato ripetutamente tentato di fare in modo che le classi implementino IDisposable in modo da abusare di questo meccanismo in modi per cui non è previsto. Ad esempio, si potrebbe implementare le classi per gestire contesti annidati in questo modo:

class Context : IDisposable 
{ 
    // Put a new context onto the stack 
    public static void PushContext() { ... } 

    // Remove the topmost context from the stack 
    private static void PopContext() { ... } 

    // Retrieve the topmost context 
    public static Context CurrentContext { get { ... } } 

    // Disposing of a context pops it from the stack 
    public void Dispose() 
    { 
     PopContext(); 
    } 
} 

uso nel chiamare il codice potrebbe essere simile a questo:

using (Context.PushContext()) 
{ 
    DoContextualStuff(Context.CurrentContext); 
} // <-- the context is popped upon leaving the block 

(Si prega di notare che questo è solo un esempio e non al argomento di questa domanda.)

Il fatto che venga richiamato lasciando l'ambito dell'istruzione using può anche essere sfruttato per implementare tutti i tipi di cose che dipendono dall'ambito, ad esempio temporizzatori. Questo potrebbe anche essere gestito utilizzando un costrutto try ... finally, ma in tal caso il programmatore dovrebbe chiamare manualmente un metodo (ad esempio Context.Pop), che il costrutto using potrebbe fare per thon.

Questo utilizzo di IDisposable non coincide con lo scopo previsto as stated in the documentation, tuttavia la tentazione persiste.

Ci sono ragioni concrete per illustrare che questa è una cattiva idea e dissipare le mie fantasie per sempre, ad esempio complicazioni con garbage collection, gestione delle eccezioni, ecc. O dovrei andare avanti e concedermi abusando di questo concetto di linguaggio in questo modo ?

+5

Ci sono molti altri concetti che potrebbero essere utilizzati impropriamente, il precedente è uno di questi, l'unica cosa è ** leggibilità **, ricorda che il tuo codice dovrebbe essere chiaro/leggibile e manutenibile da altri sviluppatori. – Habib

+4

Sono d'accordo con Habib. Pensa sempre al povero bastardo che dovrà lavorare con il tuo codice tra 18 mesi. c'è una buona possibilità che quel bastardo sia te :-) –

+1

È una domanda soggettiva, ma potresti notare che il framework MVC di ASP.NET abusa di IDisposable in questo modo - ad esempio 'FormExtensions.BeginForm' – Joe

risposta

10

Quindi, in asp.net vista MVC, vediamo la seguente costrutto:

using(Html.BeginForm()) 
{ 
    //some form elements 
} 

Un abuso? Microsoft dice no (indirettamente).

Se si dispone di un costrutto che richiede che succeda qualcosa, una volta che hai finito con esso, IDisposable spesso può lavorare fuori abbastanza bene. L'ho fatto più di una volta.

+3

Simile all'ambito della transazione. – Magnus

+1

Concordato con lo spender. Viene utilizzato da Microsoft in modi che non rilasciano sempre risorse, quindi penso che sia "consentito". Detto questo, la leggibilità dovrebbe essere la vostra preoccupazione. Penso che 'BeginForm' sia chiaro; Popping da una lista, non così tanto. –

+0

Se la riga 'using' contiene" Push ", direi che popping è abbastanza ovvio. – Medinoc

3

"È un abuso dell'interfaccia IDisposable utilizzarlo in questo modo"? Probabilmente.

L'utilizzo di using come un costrutto puramente "scoping" crea intenti più evidenti e una migliore leggibilità del codice? Certamente.

Quest'ultimo trionfa sul primo per me, quindi dico di usarlo.

+0

Perché non usare semplicemente 'try/finally'? È altrettanto leggibile. –

+0

@ChrisDunaway in questo scenario non sarei d'accordo, principalmente perché (a) in questo caso si finirebbe con un 'finally' e un (b) IMO vuoti perché' using' dichiara l'intento dello scope molto più chiaramente di 'try/finally '. "Per me" dice "Sto facendo qualcosa con questo oggetto e voglio che vada via dopo"; 'try/finally' dice" Potrei ricevere un'eccezione qui e voglio gestirla ". –

+0

Non sarei d'accordo con parte della tua dichiarazione. 'try/finally' (senza clausola catch) non implica nulla sulle eccezioni. Alla fine avrò il codice di pulizia necessario e dice "Voglio fare il follow e poi pulire". Almeno è così che lo leggerei. –

0

Sicuramente non saresti il ​​primo a "abusare" in modo IDisposable in quel modo. Probabilmente il mio preferito l'uso di esso è in timer, come dimostra StatsD.NET client:

using StatsdClient; 
... 
using (statsd.LogTiming("site.db.fetchReport")) 
{ 
    // do some work 
} 
// At this point your latency has been sent to the server 

In realtà, sono abbastanza sicuro che Microsoft si utilizzano in alcune librerie. La mia regola generale sarebbe - se migliora la leggibilità, provaci.

+0

Perché non usare semplicemente 'try/finally'? È altrettanto leggibile. –

+0

@ChrisDunaway Il punto qui è di cronometrare l'operazione all'interno del blocco using, non proteggendo dalle eccezioni. Puoi leggerlo come "using myTimer, do ..." e, così facendo, condensare quelle che altrimenti sarebbero tre righe di codice (timer.Avvio, timer.Stop, timer.SendToServer) in qualcosa (discutibilmente) più leggibile. – georgevanburgh

Problemi correlati