Cosa succede a un oggetto IDisposable creato nel mezzo di un'istruzione che non posso esplicitamente chiamare Dispose ()?

StackOverflow https://stackoverflow.com/questions/415313

Domanda

Suppongo di lavorare con Sharepoint (questo vale anche per altri modelli di oggetti) e nel mezzo della mia affermazione, chiamo un metodo, in questo caso "OpenWeb ()" che crea un oggetto SPWeb IDisposable . Ora, non posso chiamare Dispose () sull'oggetto SPWeb perché non ne ho il riferimento. Quindi devo preoccuparmi di questa perdita di memoria?

SPUser spUser = SPControl.GetContextSite(HttpContext.Current).OpenWeb().SiteUsers[@"foo\bar"];

So che avrei potuto suddividere l'istruzione in più righe e ottenere il riferimento SPWeb per chiamare Dispose:

SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb();
SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
spWeb.Dispose();

Tieni presente che la mia domanda non riguarda l'estetica, ma di più su cosa succede all'oggetto IDisposable su cui non posso esplicitamente chiamare Dispose (), poiché non ho il riferimento.

Mi dispiace non essere abbastanza chiaro quando ho posto la domanda per la prima volta. Da allora l'ho riformulato. Grazie per tutte le risposte finora.

È stato utile?

Soluzione

" cosa succede all'oggetto IDisposable che non posso chiamare esplicitamente Dispose () su? "

In generale puoi chiamare Dispose (implicitamente con un'istruzione using o esplicitamente) su tutti gli oggetti usa e getta, tuttavia nello scenario ipotetico in cui non puoi , dipende sul modo in cui l'oggetto è stato implementato.

In generale, gli oggetti .Net seguiranno lo schema lungo queste linee . Lo schema consiste nel definire un finalizzatore che pulisce le cose nel caso in cui non venga chiamato dispose e quindi disporre di eliminare il finalizzatore. Ciò riduce il carico di memoria e dà al GC meno lavoro da fare.

Tra i molti problemi con la chiamata a Dispose dal finalizzatore, c'è che stai trasformando un singolo problema con thread in un problema con multithread, il finalizzatore verrà eseguito su un thread diverso che può esporre bug difficili da catturare. Inoltre, significa che rimarrai con le risorse non gestite più a lungo del previsto (ad es. Apri un file, dimentica di chiamare chiudi o elimina e la prossima volta che vai ad aprirlo è bloccato)

La linea di fondo è, è una best practice smaltire tutti gli oggetti usa e getta, altrimenti è possibile introdurre bug strani e complessi. Con l'avvertenza che alcuni framework, come sharepoint, restituiscono istanze condivise di oggetti che non devono essere disposti secondo la documentazione.

Di solito trovo il codice molto più leggibile quando dispongo i miei oggetti con " usando " modello. Il problema con la chiamata a Dispose esplicitamente (object.Dispose ()) è che può essere difficile tracciare dove è stato allocato l'oggetto ed è molto facile dimenticarlo. Non puoi dimenticare di chiudere la parentesi graffa dell'istruzione using, il compilatore si lamenterà :)

MODIFICA / GOTCHA

Secondo Documentazione MS Non dovresti chiama dispose su riferimenti per condividere oggetti condivisi point restituiti da GetContextSite. Quindi, dovresti stare molto attento qui.

Vedi questa risposta per un modello di sharepoint sicuro che dovresti usare.

  

Tuttavia, se si ha un riferimento a   risorsa condivisa, ad esempio quando   L'oggetto è fornito da   Metodo GetContextSite in una web part,   non utilizzare nessuno dei due metodi per chiudere il file   oggetto. Utilizzando entrambi i metodi su a   la risorsa condivisa provoca un accesso   Si è verificato un errore di violazione. Negli scenari   dove hai un riferimento a un condiviso   risorsa, invece lascia Windows   SharePoint Services o il tuo portale   l'applicazione gestisce l'oggetto.

Altri suggerimenti

Quanto segue è più idiomatico e legge anche meglio:

using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
    SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
}

Dovresti chiamare esplicitamente (o implicitamente tramite usando ) il metodo Dispose. Un altro motivo per dividere il codice su più righe sono:

  • leggibilità
  • è più facile eseguire il debug

Il metodo Dispose può essere eseguito nel finalizzatore, ma è più sicuro chiamarlo da soli.

Suggerirei di dividere la linea e usare Dispose. Se un oggetto implementa IDisposable, devi presumere che richieda lo smaltimento e quindi utilizzarlo in un blocco using.

using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
    SpUser spUser = null;
    if (spWeb != null)
    {
        spUser = spWeb.SiteUsers[@"foo\bar"];
    }
}

In questo modo si arriva a disporre l'oggetto e gestire anche gli errori nella chiamata OpenWeb () che apre una risorsa esterna.

Perdita di memoria? No, non dovresti preoccuparti, supponendo che l'implementazione di IDisposable rispetti le linee guida della libreria di classi, poiché la prossima raccolta dei rifiuti dovrebbe ripulirla.

Tuttavia, rivela un errore nel tuo codice, poiché non gestisci correttamente la durata delle implementazioni di IDisposable con cui lavori (approfondisco questo problema su http://www.caspershouse.com/post/A-Better-Implementation-Pattern-for-IDisposable aspx ). Il tuo secondo blocco di codice è un buon primo passo, ma non garantisci la chiamata di Dispose se la chiamata a SiteUsers fallisce.

Il tuo codice modificato sarebbe simile al seguente:

// Get the site.
var contextSite = SPControl.GetContextSite(HttpContext.Current);

// Work with the web.
using (SPWeb web = contextSite.OpenWeb())
{
  // Get the user and work with it.
  SPUser spUser = web.SiteUsers[@"foo\bar"];
}

Come è stato detto da alcuni finora: non devi mai disporre oggetti che non crei. Quindi, nel tuo esempio, dovresti non eliminare l'oggetto SPWeb o SPSite!

Ciò distruggerà l'oggetto SPRequest corrente. Potrebbe sembrare che funzioni ancora, ma se ad esempio aggiungi nuove web part in un secondo momento o provi ad aprire il riquadro degli strumenti per la tua web part, otterrai strani tipi di errori.

Come è già stato detto, è necessario disporre delle istanze di SPWeb e SPSite create dall'utente (nuovo).

Questo può essere fatto con using () o con try / finally (che è il modo in cui un'istruzione using () apparirà comunque nel tuo codice MSIL). Se si utilizza try / infine è consigliabile verificare la presenza di null sull'istanza SPWeb / SPSite e verificare prima SPWeb, poiché SPSite eliminerà automaticamente SPWeb.

Un'altra cosa importante da ricordare è quando esegui il loop di SPWebCollections come AllWebs o Webs è quello di disporre i tuoi Web secondari mentre li attraversi. Se sono presenti molti Web secondari e si esegue hardware a 32 bit con un potenziale di memoria limitato, è possibile riempire la memoria con oggetti SPRequest molto velocemente. Ciò comporterà prestazioni lente poiché causerà il riciclo regolare del pool di applicazioni.

Detto questo, è anche consigliabile non combinare le chiamate come fai nell'esempio di codice. È difficile da leggere e se stavi lavorando con un SPWeb che dovresti smaltire, non potresti! Questo tipo di perdite di memoria sono le più difficili da individuare, quindi non farlo ;-)

Posso consigliare il blog di Roger Lamb per i dettagli: http://blogs.msdn.com/rogerla Anche alcuni dettagli tecnici sul blog di Stefan Goßner: http: // blog. technet.com/stefan_gossner/archive/2008/12/05/disposing-spweb-and-spsite-objects.aspx

hth Anders

Molte risposte qui assumono che sia importante solo che alla fine venga chiamato Dispose. Quando lavori con SPSite e SPWeb, tuttavia, devi assolutamente chiamare Dispose () il prima possibile. Determinare quando dovresti spesso è complicato, ma ci sono molti buone referenze disponibili per aiutare a rispondere a questa domanda.

Sul perché di questo caso, Stefan Goßner fornisce un ottimo riassunto qui :

  

Ogni oggetto SPWeb e SPSite contiene un   riferimento a un oggetto SPRequest che   contiene un riferimento a una COM di SharePoint   oggetto di cui è responsabile   comunicare con il backend SQL   server.

     

Lo smaltimento di un oggetto SPWeb non lo farà   rimuovere effettivamente l'oggetto SPWeb da   memoria (in realtà il framework .NET   non consente di rimuovere alcun oggetto   dalla memoria in modo deterministico)   ma chiamerà un metodo di SPWeb   oggetto che causa l'oggetto COM   chiudere la connessione al server SQL   e per liberare la memoria allocata.

     

Ciò significa che la connessione a   il server SQL back-end rimarrà aperto   dal momento in cui l'oggetto SPRequest   è stato creato fino all'oggetto SPWeb   è eliminato.

La migliore pratica per il tuo esempio di codice sarebbe simile a questa:

SPSite contextSite = SPControl.GetContextSite(HttpContext.Current);
using (SPWeb spWeb = contextSite.OpenWeb())
{
  SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
  // Process spUser
}
// DO NOT use spUser
// DO NOT dispose site from HttpContext

Nota che non è sicuro usare oggetti SP come SPUser, SPList, ecc. dopo che il loro SPWeb padre è stato eliminato.

Da http://msdn.microsoft.com/en-us/ library / aa973248.aspx Best practice: utilizzo di oggetti monouso di Windows SharePoint Services :

Se l'oggetto SPSite è ottenuto da SPControl.GetContextSite, l'applicazione chiamante NON deve disporre dell'oggetto. Poiché gli oggetti SPWeb e SPSite mantengono un elenco interno derivato in questo modo, la disposizione dell'oggetto può comportare un comportamento imprevedibile del modello a oggetti di SharePoint.

Nessuno sembra averlo ancora lanciato:

Se il tuo oggetto richiede un disposer (ovvero acquisisce risorse che devono essere rilasciate), devi implementare un finalizzatore che chiama il metodo del disposer.

Nel separatore è possibile aggiungere la riga:

System.GC.SuppressFinalize(this)

Ciò impedirà la finalizzazione se si chiama lo smaltimento. In questo modo puoi usare il tuo oggetto bene se necessario, ma garantire che ripulisca dopo se stesso tramite il finalizzatore (che è l'intera ragione per cui C # ha un finalizzatore).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top