L''uso' è appropriato in un contesto in cui non vi è nulla da smaltire?

9

In C #, l'istruzione using viene utilizzata per disporre in modo deterministico le risorse senza attendere il garbage collector. Ad esempio, può essere usato per:

  • Elimina comandi o connessioni SQL,

  • Chiudi stream, liberando l'origine sottostante come un file,

  • Elementi GDI + gratuiti,

  • ecc.

Ho notato che using viene utilizzato sempre di più nei casi in cui non c'è nulla da smaltire, ma dove è più conveniente per il chiamante scrivere un blocco using anziché due comandi separati.

Esempi:

  • MiniProfiler , scritto dal team Stack Overflow, utilizza using per indicare i blocchi del profilo:

    using (profiler.Step("Name goes here"))
    {
        this.DoSomethingUseful(i - 1);
    }
    

    Un approccio alternativo sarebbe avere due blocchi:

    var p = profiler.Start("Name goes here");
    this.DoSomethingUseful(i - 1);
    profiler.Stop(p);
    

    Un altro approccio sarebbe utilizzare le azioni:

    profiler.Step("Name goes here", () => this.DoSomethingUseful(i - 1));
    
  • ASP.NET MVC ha anche selezionato using per i moduli:

    <% using (Html.BeginForm())
       { %>
           <label for="firstName">Name:</label>
           <%= Html.TextBox("name")%>
           <input type="submit" value="Save" />    
    <% } %>
    

Tale uso è appropriato? Come giustificarlo, dato che ci sono diversi svantaggi:

  • I principianti sarebbero persi, poiché tale uso non corrisponde a quello spiegato nei libri e nelle specifiche del linguaggio,

  • Il codice dovrebbe essere espressivo. Qui, l'espressività soffre, poiché l'uso appropriato di using è mostrare che dietro c'è una risorsa, come un flusso, una connessione di rete o un database che dovrebbe essere rilasciato senza attendere il garbage collector.

posta Arseni Mourzenko 24.01.2013 - 21:36
fonte

3 risposte

5

La tua ultima affermazione - che "l'uso appropriato dell'uso è quello di mostrare che dietro c'è una risorsa, come un flusso, una connessione di rete o un database che dovrebbe essere rilasciato senza attendere che il garbage collector" non sia corretto, e Motivo per cui viene fornito nella documentazione dell'interfaccia IDisposable: link

The primary use of this interface is to release unmanaged resources.

Quindi, se la tua classe utilizza risse non gestite, non importa quando potresti o non vuoi che GC accada: non ha assolutamente nulla a che fare con GC in quanto le risorse non gestite non sono GC ( link ).

Quindi lo scopo di "usare" non è quello di evitare di aspettare su GC, è forzare un rilascio di quelle risorse non gestite ora , prima che l'istanza di classe vada fuori ambito e si chiama Finalize. Questo è importante per ragioni che dovrebbero essere ovvie: una risorsa non gestita può avere dipendenze da altre risorse non gestite e se vengono eliminate (o finalizzate) nell'ordine sbagliato possono accadere cose brutte.

Quindi, se la classe istanziata nel blocco using utilizza risorse non gestite, allora la risposta è sì - è appropriato.

Nota che IDisposable non è prescrittivo perché è solo per il rilascio di risorse non gestite, tuttavia - solo che questo è lo scopo primario . può essere il caso che l'autore della classe abbia qualche azione che vuole far rispettare in un dato momento, e implementare IDisposable può essere un modo per farlo accadere, ma se questo sia o meno una soluzione elegante è qualcosa a cui è possibile rispondere solo caso per caso.

Indipendentemente da ciò, l'uso di "using" implica che la classe implementa IDisposable, quindi non viola l'espressività del codice; rende abbastanza chiaro cosa sta succedendo, infatti.

    
risposta data 24.01.2013 - 22:17
fonte
8

L'uso di using implica la presenza di un metodo Dispose() . Altri programmatori supporteranno che un tale metodo esista sull'oggetto. Di conseguenza, se un oggetto non è usa e getta, non dovresti usare using su di esso.

La chiarezza del codice è re. O ometti using o implementa IDisposable sull'oggetto.

Sembra che MiniProfiler utilizzi using come meccanismo per "recintare" il codice che viene profilato. C'è qualche merito a questo; presumibilmente, MiniProfiler chiama o Dispose() per arrestare un timer, oppure il timer viene fermato quando l'oggetto MiniProfiler esce dall'ambito.

Più in generale, invocare using quando è necessario eseguire automaticamente una sorta di finalizzazione. La documentazione per html.BeginForm indica che, quando il metodo viene utilizzato in un'istruzione using , esegue il rendering del tag di chiusura </form> alla fine del blocco using .

Questo non significa necessariamente che non sia ancora un abuso.

    
risposta data 24.01.2013 - 21:41
fonte
1

using è errore ed eccezionalmente sicuro. Assicura che Dispose() sarà chiamato senza errori di errori del programmatore. Non interferisce con il rilevamento o la gestione delle eccezioni, ma il metodo Dispose() viene eseguito in modo ricorsivo sullo stack quando viene generata un'eccezione.

Oggetti che implementano IDispose ma che non hanno nulla da smaltire quando vengono completati. Sono al meglio, a prova di futuro il loro design. In modo che tu non debba refactoring il tuo codice sorgente, quando in futuro, hanno quindi bisogno di smaltire qualcosa.

    
risposta data 24.01.2013 - 22:43
fonte

Leggi altre domande sui tag