2009-04-03 7 views
7

Sono un po 'nuovo in C#, più abituato ai linguaggi di scripting. Mi piace l'idea di 'usare', istanziare un oggetto, e poi tu operi entro il suo campo di azione finché ne hai bisogno, poi ti lasci che si disfi da solo quando ha finito il suo scopo.Come faccio a sapere qual è il posto migliore in cui usare "usando"?

Ma non è naturale per me. Quando le persone mi mostrano esempi che lo usano, lo riconosco come un buon strumento per il lavoro, ma non mi viene mai in mente di risolvere i problemi con esso nella mia programmazione personale.

Come è possibile riconoscere i punti validi per l'uso di using e come utilizzarlo in combinazione con i blocchi try-catch. Entrano nel blocco o di solito si desidera includere un'istruzione using all'interno di un blocco try?

risposta

9

Ho raramente scrivere provare/blocchi catch - la maggior parte delle eccezioni vengono gettati fino a (quasi) in cima alla pila. Se I do ha bisogno di un blocco try/catch, non sono sicuro di essere particolarmente coerente tra l'inserimento nella dichiarazione using e all'esterno. Dipende veramente dal fatto che si desideri che la risorsa venga eliminata prima che venga eseguito il codice di gestione delle eccezioni.

Se stai chiedendo circa quando si dovrebbe scrivere using dichiarazioni - ogni volta che si "proprio" un oggetto che implementa IDisposable (direttamente o indirettamente attraverso l'ereditarietà) e controllare la sua vita. Di solito è un oggetto che utilizza una risorsa non gestita come un handle di file o una connessione di rete. Non è sempre molto ovvio, ma impari attraverso l'esperienza. Quasi qualsiasi cosa che abbia da fare con IO sarà eliminabile e maniglie Windows (per i caratteri ecc.) Sono simili.

+0

Per chiarire gli oggetti che implementano IDisposable direttamente o tramite l'ereditarietà (dato che danieltalsky è "un po 'nuovo per C#"). –

+0

Grazie Jonathan - modificato in modo appropriato. –

+0

Beh, per "un po 'nuovo per C#" intendo, "ho scritto codice commerciale in C# per meno di 8 mesi". Ad esempio, avevo già familiarità con la connessione di "usare" con "IDisposable" che menziono nella domanda. Era la parte del "controllo a vita" che mi mancava. – danieltalsky

18

using può essere utilizzato solo con i tipi che implementano IDisposable; garantisce che il metodo Dispose() sarà chiamato anche se si verifica un errore.

Questo codice:

using (MyDisposableType x = new MyDisposableType()) 
{ 
    // use x 
} 

è equivalente a questo:

MyDisposableType x = new MyDisposableType(); 
try 
{ 
    // use x 
} 
finally 
{ 
    x.Dispose(); 
} 
+0

Quindi ... usarlo su qualsiasi oggetto che implementa IDisposable? Sempre? – danieltalsky

+0

E per quanto riguarda la gestione delle eccezioni? – danieltalsky

+0

fare downvoters si prega di lasciare un commento. Grazie. –

1

coSa Mitch ha detto, più ..

È possibile utilizzare l'istruzione using all'esterno o all'interno di un blocco try..catch sarebbe davvero dipenderà da ciò che si sta cercando di ottenere cioè se si potrebbe aspettare qualcosa di gettare un eccezione durante l'utilizzo di un particolare oggetto da cui si prevede di recuperare, ad esempio.

Allo stesso modo, è possibile anche eliminare un oggetto che implementa IDisposable in un blocco finally se necessario.

3

Io penso di usarlo come "Usalo ogni volta che il tipo implementa IDisposable e non si sta più andando a richiedere questa particolare istanza".

1

Utilizzare utilizzando quando è necessario disporre di un oggetto deterministico. Ad esempio, se apri un file, il file è bloccato. Spesso vorrai che il file venga chiuso il prima possibile in modo che altri programmi possano accedervi. Se non si utilizza utilizzando e scrivere smth come:

System.IO.FileStream writeStream = new System.IO.FileStream(fileName, System.IO.FileMode.OpenOrCreate)); 
System.IO.BinaryWriter writer = new System.IO.BinaryWriter(writeStream)); 
//do smth 

e si verifica un'eccezione durante il "fare smth" non si sa quando gli oggetti che operano su un file sono in realtà disposti e file viene chiuso.Con utilizzando si sa per certo che una volta che hai lasciato la utilizzando blocco comunicato - direttamente o tramite un'eccezione l'oggetto nella useing dichiarazione è disposto chiamando IDisposable :: Dispose:

using(System.IO.FileStream writeStream = new System.IO.FileStream(fileName, System.IO.FileMode.OpenOrCreate)) { 
    using(System.IO.BinaryWriter writer = new System.IO.BinaryWriter(writeStream)) { 
     //do stmth 
    } 
} 
+0

No, l'oggetto * non è * finalizzato. È disposto solo. La finalizzazione e lo smaltimento sono molto diversi. Di solito lo smaltimento sopprimerà un finalizzatore (se ce n'è uno) ma non è la stessa cosa. –

+0

"Utilizzare l'utilizzo quando è necessario disporre di un oggetto deterministico." Mi sembra che se un oggetto implementa IDisposable lo si debba sempre disporre se l'oggetto non è più necessario. Implementando IDisposable l'oggetto dice "I (potrebbe) utilizzare le risorse che devono essere smaltite". –

+0

Cosa succede se l'implementazione dell'oggetto in questione cambia ed elimina immediatamente diventa fondamentale? Qual è il vantaggio del non smaltimento subito dopo l'uso? –

1

Puoi racchiudere un uso all'interno di un blocco try/catch e puoi racchiudere un blocco try/catch all'interno di un using.

Una situazione in cui utilizzando è bello è quando si sta facendo operazioni di database utilizzando DBConnection e DBCommands:

using (SqlConnection conn = new SqlConnection(connectionString)) 
using (SqlCommand command = new SqlCommand(sql, conn)) 
{ 
    // do something here 
} 

Ora, quando si lasciano i blocchi utilizzando il vostro comando è disposto e la connessione viene chiusa.

1

Quello che ha detto Mitch è giusto. Quindi l'uso principale dell'uso è quello di garantire che gli oggetti IDisposable vengano smaltiti, senza che devono codificare un'istruzione try/catch o try/finally.

Ora, c'è un uso più avanzato che potresti trovare interessante. Quando si utilizza un'istruzione using, il compilatore genera un try/finally e genera anche una chiamata a Dispose() per l'utente all'interno del file finalmente generato. Puoi usare questo metodo Dispose() come un "hook" per fare tutto ciò che vuoi ... non deve essere correlato al rilascio di risorse.

Ad esempio, Jeffrey Richter utilizza questo in un oggetto timer che ha scritto. Si può fare qualcosa di simile con esso (concettuale soltanto):

using(var x = new Timer()) 
{ 
    // Do the operation you want timed 
} 

// Now, the timer has been stopped, because the 
// compiler-generated call to Dispose() has already 
// occurred, and Jeffrey uses Dispose() to stop 
// the timer. 
+0

Sembra un po 'complicato. Dopo la parentesi di chiusura non hai accesso a x. Come puoi quindi approfittare del timer fermo? –

+0

Sì, è vero. Nel caso del timer di Richter, emette il tempo trascorso (e il numero di garbage collection che si sono verificati) nella console. Quindi non ha avuto altro bisogno di fare riferimento all'oggetto dopo la parentesi di chiusura. –

+0

Tuttavia, ci sono modi per farlo anche se lo si desidera. Si potrebbe dire all'oggetto di sparare un evento o mettere risultati in una collezione. Oppure puoi parametrizzare l'oggetto con un'azione lambda da eseguire al suo interno. Ma questo sta diventando troppo lontano dalla domanda originale. –

2

Se volete sapere come utilizzare in modo creativo all'interno vostri propri disegni, guardare attraverso alcuni il proprio codice per situazioni in cui una particolare porzione di codice assolutamente deve essere eseguito prima che il blocco di chiusura venga chiuso. Queste sono le situazioni in cui try/finally o using possono aiutarti.

In particolare, se si è tentato di ottenere ciò rilevando tutte le eccezioni, è necessario passare a try/finally o using.

Se il motivo si verifica più volte, è possibile creare una classe che implementa IDisposable per acquisire il motivo e consentire di richiamare il motivo con l'istruzione using. Ma se hai un caso specifico che sembra essere una tantum, usa semplicemente try/finally.

I due sono molto simili, in realtà - using è specificato in termini di try/finally, ma anche se abbiamo avuto solo using, potremmo costruire try/finally noi stessi:

public class DisposeAnything : IDisposable 
{ 
    public Action Disposer; 

    public void Dispose() 
    { 
     Disposer(); 
    } 
} 

Ora si potrebbe dire:

using (new DisposeAnything 
     { 
      Disposer =() => File.Delete(tempFileName) 
     }) 
{ 
    // blah 
} 

quale è la stessa:

try 
{ 
    // blah 
} 
finally 
{ 
    File.Delete(tempFileName); 
} 

Basta pensare a come un modo per ottenere un codice da eseguire quando si lascia un ambito.

+0

Ehi, la parametrizzazione della logica di smaltimento è un'idea interessante. Immagino ci siano molti modi per farlo. Freddo. –

+0

Portato all'estremo sopra, non c'è molto senso ad esso! Ma sì, probabilmente ci sono molti casi a metà strada. Potresti trovare interessante questa discussione: http://incrediblejourneysintotheknown.blogspot.com/2009/02/functional-replacement-for-using.html –

1

Se una classe implementa IDisposable, è probabilmente per una buona ragione. Quindi qualsiasi classe che implementa IDisposable dovrebbe essere eliminata.

1

Dato che è ciò che è noto come "zucchero sintattico" e produrrà lo stesso IL di un costrutto try/finally dispose, è davvero solo un bel modo di "short-handing" tale codice.

Mi piace usarlo per semplificare sezioni di codice in cui gli oggetti usa e getta sono molto utilizzati, ad esempio l'accesso a risorse come file e oggetti grafici e voglio essere sicuro di non dimenticare di gestire lo smaltimento della risorsa oggetto.

0

Ho notato che quando si nota il metodo Dispose, i programmatori non si preoccupano di chiamarlo come sembra inutile. Tuttavia, se l'oggetto implementa IDisposable è (si spera) per un motivo e le versioni future di tale oggetto potrebbero effettivamente avere il codice nel metodo Dispose, quindi chiamatelo sempre.

Problemi correlati