2010-01-27 6 views
6

Per esempio, nel seguente codice verrà creato un 'image'object e poi garbage collection ad un certo punto sconosciuto nel futuroÈ possibile scrivere C# in modo che gli oggetti vengano eliminati quando non rientrano nell'ambito di applicazione?

void MyFunction() { 

    Bitmap image = RetrieveImage(); 
    DoSomething(image); 
} 

Che dire

void MyFunction() { 

    DoSomething(RetrieveImage()); 

} 

in questo caso è l'oggetto raccolta dei rifiuti una volta fuori portata, vale a dire dopo la fine di MyFunction. Se no, c'è da qualche parte per far rispettare questo?

+3

non vedo alcuna differenza tra 1 und 2. Perché dovrebbe comportarsi diverso garbage collection-saggio? – flq

+2

Non sono davvero un sostenitore di "cosa vorresti questo" tipo di commenti, ma seriamente, il garbage collector .NET sa davvero meglio del programmatore quando è un buon momento per fare un po 'di pulizia. tenere presente che .NET VM non è un sistema in tempo reale. inoltre, se si dispone di risorse non gestite, utilizzare IDisposable e using. –

+0

Ci si deve fidare del garbage collector standard: è molto probabile che si perderanno prestazioni se si costringerebbe la garbage collection. C'è una ragione particolare per cui ti preoccupi di quando succede? – jball

risposta

19

No. In realtà, non si vuole veramente che si tratti di garbage collection: il prompt del garbage collector molto spesso ridurrà le prestazioni.

Quello che fai vuoi è quello di disporre delle risorse non gestite in modo tempestivo - ed è qui che IDisposable entra, insieme con la using dichiarazione:

void MyFunction() 
{ 
    using (Bitmap image = RetrieveImage()) 
    { 
     DoSomething(image); 
    } 
} 

che chiamerà image.Dispose() in quanto lascia il using, indipendentemente dal fatto che DoSomething abbia fatto o meno un'eccezione.

Non è necessario utilizzare la variabile in più anche se - a meno che non si cambia DoSomething di prendere un Func<Bitmap> invece, così invece di:

void DoSomething(Bitmap image) 
{ 
    // Code here 
} 
... 
DoSomething(RetrieveImage()); 

avresti:

void DoSomething(Func<Bitmap> imageProvider) 
{ 
    using (Bitmap image = imageProvider()) 
    { 
     // Code here 
    } 
} 
... 
DoSomething(() => RetrieveImage()); 

Si noti che che non offre l'opportunità di passare in una bitmap senza da smaltire, il che potrebbe essere un problema se si desidera utilizzarlo nuovamente in un secondo momento. Comunque, è una buona tecnica per saperlo.

MODIFICA: come sottolineato da Mbeckish nei suoi commenti, non ci sono molti vantaggi rispetto allo smaltimento della bitmap entro RetrieveImage. Ecco una variante del modello però:

public void ApplyToEachLineInFile(string file, Action<string> action) 
{ 
    using (TextReader reader = File.OpenText(file)) 
    { 
     string line; 
     while ((line = reader.ReadLine()) != null) 
     { 
      action(line); 
     } 
    } 
} 

qui la logica "acquistare e cedere" è incapsulato, senza che il chiamante preoccuparsi - ma il chiamante può ancora essere molto flessibile in termini di complessità della logica passano poll.

+0

Bel suggerimento di passare nel delegato del provider. Questo è uno schema molto utile in molti casi. –

+0

Perché è meglio di DoSomething (RetrieveImage()); e disporre la bitmap in DoSomething? – mbeckish

+0

Perché se la bitmap viene passata a DoSomething, come fa DoSomething a sapere se è sicuro smaltirlo? Cosa accadrebbe se il chiamante volesse passare la bitmap a DoSomethingElse dopo averlo passato a DoSomething? –

4

La raccolta dei dati inutili non si verifica quando un oggetto non rientra nell'ambito. Piuttosto è una funzione di gestione automatica della memoria dal framework.

http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

È possibile forzare .Net per raccogliere la spazzatura, ma a meno che non si verificano problemi di memoria gravi, io non consiglierei di andare su questa strada.

+0

concordato, di solito è meglio lasciare che il garbage collector svolgere i propri compiti automaticamente al contrario di invocare manualmente. –

0

È possibile imporre Garbage Collection con:

System.GC.Collect()

dopo lo smaltimento in modo corretto (vedi Jons Risposta per l'uso() {} - Syntax).

< Modifica>

Non dimenticare (come ho appena fatto)

System.GC.WaitForPendingFinalizers();

dopo.

</Modifica>

Anche se non è raccomandato - come ha detto Jon.

+1

La chiamata a Collect non * garantisce * che un oggetto fuori ambito venga effettivamente raccolto. Ci sono molti scenari interessanti e bizzarri in cui un tale oggetto può vivere ancora un po '. Si noti inoltre che in genere è necessario attendere la pubblicazione dei finalizzatori in sospeso se si sta forzando una raccolta; i finalizzatori in attesa vengono eseguiti su un altro thread, quindi potrebbero esserci delle corse. –

+0

Vero. Grazie.Dovrebbe aver semplicemente copiato il codice che abbiamo usato;) – Leonidas

3

NOTA: Non si vuole fare questo. Il garbage collector .NET è "più intelligente" di te (o io, o Jon Skeet o chiunque altro).

try 
{ 
    Bitmap image = RetrieveImage(); 
    DoSomething(image); 
} 
finally 
{ 
    GC.Collect(); 
} 

(questo è supponendo che nessuno degli oggetti che cadono fuori portata implementare IDisposable)

+1

Nota che per far funzionare questo è cruciale che la variabile 'image' sia dichiarata all'interno dell'istruzione' try', come indicato. Altrimenti, il runtime può o non può considerare l'oggetto non raggiungibile, e si può finire per promuoverlo a una generazione superiore, impedendo in modo efficace che l'oggetto venga mai raccolto in modo improprio. –

Problemi correlati