L '"utilizzo" è appropriato in un contesto in cui non c'è nulla da smaltire?


9

In C #, l' usingistruzione viene utilizzata per disporre in modo deterministico le risorse senza attendere il Garbage Collector. Ad esempio, può essere utilizzato per:

  • Disponi comandi o connessioni SQL,

  • Chiudi stream, liberando la sorgente sottostante come un file,

  • Elementi GDI + gratuiti,

  • eccetera.

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

Esempi:

  • MiniProfiler , scritto dal team Stack Overflow, utilizza usingper indicare i blocchi per profilare:

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

    Un approccio alternativo sarebbe quello di avere due blocchi:

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

    Un altro approccio sarebbe quello di utilizzare le azioni:

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

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

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

  • I principianti andrebbero persi, poiché tale utilizzo non corrisponde a quello che è spiegato nei libri e nelle specifiche del linguaggio,

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


2
L'approccio di avere due singole affermazioni soffre dello stesso problema della gestione ingenua delle risorse senza using: quest'ultima affermazione (ad es. profiler.Stop(p)) Non è garantita per essere eseguita a fronte di eccezioni e flusso di controllo.

1
@delnan: questo spiega perché MiniProfiler ha evitato il secondo approccio. Ma il secondo dovrebbe essere evitato in tutti i casi, poiché è difficile da scrivere e mantenere.
Arseni Mourzenko,



Risposte:


7

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 il garbage collector" non è corretta, e il motivo per cui è fornito nella documentazione per l'interfaccia IDisposable: http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

L'uso principale di questa interfaccia è di rilasciare risorse non gestite.

Quindi, se la tua classe utilizza risorse non gestite, non importa quando potresti o meno voler fare GC - non ha nulla a che fare con GC poiché le risorse non gestite non sono GC ( https: // stackoverflow. com / questions / 3607213 / what-is-mean-by-managed-vs-unmanaged-resources-in-net ).

Quindi lo scopo di "usare" non è evitare l'attesa su GC, è forzare un rilascio di quelle risorse non gestite ora , prima che l'istanza della classe esca dall'ambito e venga chiamato Finalize. Questo è importante per ragioni che dovrebbero essere ovvie: una risorsa non gestita può avere dipendenze da altre risorse non gestite e se vengono smaltite (o finalizzate) nell'ordine sbagliato possono accadere cose Cattive.

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

Si noti che IDisposable non è prescrittivo del fatto che sia solo per il rilascio di risorse non gestite, solo che questo è il suo scopo principale . Essa può essere il caso che l'autore della classe ha una certa azione che vogliono far rispettare accadendo in un determinato momento, e l'attuazione IDisposable può essere un modo di ottenere che ciò accada, ma se non che è una soluzione elegante è qualcosa che può ricevere una risposta solo caso per caso.

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


"L'uso principale di questa interfaccia è di rilasciare risorse non gestite." Dico che la documentazione di Microsoft fa schifo su questo punto. Forse è così che lo usano nella FCL, ma gli sviluppatori di applicazioni lo usano da una varietà di cose da quasi un decennio ormai - alcuni per buoni motivi, altri per ragioni non così buone. Ma citando quel pezzo del loro documento non risponde affatto alla domanda del PO.
Jesse C. Slicer,

1
Nota il mio ultimo paragrafo: lo scopo principale non deve essere l' unico scopo. IDisposable può essere implementato per qualsiasi motivo e fornisce un'interfaccia nota con il comportamento e lo stile di utilizzo previsti, quindi quando lo vedi sai che c'è qualcos'altro che deve accadere prima che l'istanza possa essere lasciata fuori dal campo di applicazione. Il punto è: se implementa IDisposable, allora usare è appropriato; tutto il resto è solo preambolo.
Maximus Minimus,

1
In pratica stai dicendo che lo scopo non è evitare di aspettare GC, ma evitare di aspettare il finalizzatore. Ma non vedo la differenza: il finalizzatore viene chiamato dal GC.
svick,

Il GC funziona in modo non deterministico - non sai quando verrà chiamato. Il GC chiama anche il finalizzatore, ma non fa alcuno smaltimento: la risorsa stessa è al di fuori del controllo del GC. Con l'utilizzo di (1) l'oggetto è correttamente definito, e (2) è noto che lo smaltimento avviene in un momento noto - quando esce dall'ambito del blocco utilizzante, la risorsa non gestita è andata (o - nel peggiore dei casi - consegnata a qualcos'altro che lo distruggerà). Davvero, questo è solo un pignolo; leggi la documentazione, è tutto ciò che c'è non dovrebbe essere ripetuto.
Maximus Minimus

8

L'uso di usingimplica la presenza di un Dispose()metodo. Altri programmatori presumeranno che un tale metodo esista sull'oggetto. Di conseguenza, se un oggetto non è usa e getta, non è necessario utilizzarlo using.

La chiarezza del codice è re. O omettere il using, o implementare IDisposablesull'oggetto.

MiniProfiler sembra utilizzare usingun meccanismo per " bloccare " il codice che viene profilato. C'è qualche merito in questo; presumibilmente, MiniProfiler sta chiamando Dispose()per arrestare un timer oppure il timer viene arrestato quando l'oggetto MiniProfiler esce dall'ambito.

Più in generale, invocheresti usingquando una sorta di finalizzazione deve avvenire automaticamente. La documentazione per html.BeginFormafferma che, quando il metodo viene utilizzato in usingun'istruzione, esegue il rendering del </form>tag di chiusura alla fine del usingblocco.

Ciò non significa necessariamente che non sia ancora un abuso.


4
Fin qui tutto ovvio. La domanda è: quando (se non del tutto) è appropriato implementare IDisposable/ Dispose()nonostante non abbia nulla da smaltire nel senso che OP sta descrivendo?

2
Pensavo di averlo chiarito; non c'è un momento in cui ciò sia appropriato.
Robert Harvey,

1
Il mio punto è che Disposeè necessario per usingavere un senso (indipendentemente dal fatto che sia appropriato). Gli esempi di OP sono tutti implementati Dispose, vero? E IIUC, la domanda è se è corretto che queste classi utilizzino Dispose, piuttosto che qualche altro metodo (come Stop()per MiniProfiler).

1
Sì, ma non è questa la domanda. La domanda è se è una buona idea farlo.

2
@delnan: Ancora una volta, ho pensato di chiarirlo. Se rende più chiaro l'intento del tuo codice, allora è una buona idea. In caso contrario, non lo è.
Robert Harvey,

1

usingl'errore e l'eccezione sono sicuri. Assicura Dispose()che verrà chiamato indipendentemente da eventuali errori commessi dal programmatore. Non interferisce con la cattura o la gestione delle eccezioni, ma il Dispose()metodo viene eseguito in modo ricorsivo nello stack quando viene generata un'eccezione.

Oggetti che implementano IDisposema non hanno nulla da smaltire al termine. Nel migliore dei casi, sono a prova di futuro il loro design. In modo che non sia necessario riformattare il codice sorgente, quando in futuro devono disporre di qualcosa.

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.