Am citit Microsoft documentația că "primar" de IDisposable` interfață este de a curăța unmanaged resurse.
Pentru mine, "unmanaged" înseamnă lucruri cum ar fi conexiuni la baze de date, prize, mânere de fereastră, etc. Dar, am'am văzut un cod unde `Dispose () metoda este pusă în aplicare pentru a elibera managed resurse, care se pare redundant pentru mine, deoarece colectorul de gunoi ar trebui să aibă grijă de asta pentru tine.
De exemplu:
public class MyCollection : IDisposable
{
private List<String> _theList = new List<String>();
private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();
// Die, clear it up! (free unmanaged resources)
public void Dispose()
{
_theList.clear();
_theDict.clear();
_theList = null;
_theDict = null;
}
Intrebarea mea este, acest lucru face gunoier gratuit de memorie folosit de MyCollection
mai repede decât în mod normal?
edit: până acum oamenii au postat câteva exemple bune de a utiliza IDisposable pentru a curăța unmanaged resurse, cum ar fi conexiuni la baze de date și bitmap. Dar să presupunem că `_theList în codul de mai sus conținea un milion de siruri de caractere, și ai vrut pentru a elibera memorie now, mai degrabă decât de așteptare pentru colectorul de gunoi. Ar fi codul de mai sus realiza asta?
Punctul de a Dispune este pentru a elibera resurse unmanaged. Acesta trebuie să fie făcut la un moment dat, în caz contrar ele nu vor fi curățate. Colectorul de gunoaie nu't știu cum pentru a apela DeleteHandle () într-o variabila de tip
IntPtr, nu't știu **fie** sau nu este nevoie pentru a apela
DeleteHandle()`.
Notă: Ce este un resurse unmanaged? Dacă ai găsit-o în Microsoft .NET Framework: it's a reușit. Dacă ai fost poking jurul valorii de MSDN-te, de's unmanaged. Orice ai'am folosit P/Invoke apeluri pentru a ieși din zona confortabilă lume de toate disponibile pentru a vă în .NET Framework este unmanaged – și te're acum responsabil pentru curățenie. Obiectul de care te'am creat are nevoie pentru a expune unele metoda, pe care lumea exterioară poate apela, în scopul de a curăța unmanaged resurse. Metoda poate fi numit orice doriți:
public void Cleanup()
sau
public void Shutdown()
Dar în schimb există o standardizate nume pentru această metodă:
public void Dispose()
A existat chiar și o interfață creată, IDisposable
, care are doar o singură metodă:
public interface IDisposable
{
void Dispose()
}
Asa ca ai face-ți de obiect expune IDisposable
de interfață, și că modul în care promit că te'am scris că o singură metodă pentru a curăța unmanaged resurse:
public void Dispose()
{
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}
Dacă obiectul are alocat un 250MB [Sistem.Desen.Bitmap][1] (adică .NET reușit Bitmap clasa) ca un fel de frame buffer? Sigur, aceasta este o reușit .NET obiect, și colectorul de gunoi va elibera. Dar chiar vrei să pleci de 250MB memorie doar stând acolo în așteptare pentru colectorul de gunoi de la până la urmă vino de-a lungul și de-l gratuit? Ce se întâmplă dacă nu's [deschide conexiunea la baza de date][2]? Cu siguranță nu ne't vreau conexiune sta deschis, de așteptare pentru GC pentru a finaliza obiect.
Dacă utilizatorul a numit Dispose()
(în sensul că nu mai au de gând să utilizați obiect) de ce nu a scăpa de aceste risipitor bitmaps și conexiuni la baza de date?
Deci, acum vom:
public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//Free managed resources too
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose();
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose();
this.frameBufferImage = null;
}
}
Dacă persoana uitat pentru a apela Dispose()
pe obiect? Atunci ei s-ar scurge un unmanaged resurse!
Notă: - Au câștigat't scurgere a reușit resurse, pentru că în cele din urmă colectorul de gunoaie este de gând să ruleze, pe un fir de fundal, și de a elibera memorie asociate cu orice obiecte nefolosite. Aceasta va include obiect, și orice obiecte gestionate utilizați (de exemplu,
Bitmap " și " DbConnection
). Dacă persoana uitat să-l sunDispose()
, putem încă salva pielea lor! Avem încă o modalitate de a face apel pentru a i: când colectorul de gunoi devine în cele din urmă în jurul pentru a elibera (de exemplu, finalizarea) obiectul nostru. Notă: colectorul De gunoi în cele din urmă va elibera toate obiecte gestionate. Când o face, se solicităDefinitiva
metoda pe obiect. GC nu't știu, sau îngrijire, despre ta Dispune metoda. Asta a fost doar un nume pe care am ales-o pentru o metodă numim atunci când vrem să ajungem scape de unmanaged lucruri. Distrugerea obiectul nostru de colectorul de Gunoaie este perfect timp pentru a elibera aceste plictisitor unmanaged resurse. Facem acest lucru prin imperativeFinalize () metodă. **Notă:** În C#, nu't în mod explicit suprascrie
Finalize () metodă. scrie o metodă care se pare o C++ destructor, și compiler nevoie ca să-ți fie punerea în aplicare aFinalize()
metoda:
~MyObject()
{
//we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
Dispose(); //<--Warning: subtle bug! Keep reading!
}
Dar nu's un bug în cod. Vezi tu, colectorul de gunoi ruleaza pe un fir de fundal; nu't știu ordinea în care două obiecte sunt distruse. Este foarte posibil ca în Dispose()
cod, gestionată obiect're încercarea de a scăpa de (pentru că ai vrut să fie de ajutor) nu mai există:
public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);
//Free managed resources too
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
this.frameBufferImage = null;
}
}
Deci, ce ai nevoie este o modalitate pentru a Finaliza()a spune
Dispose()că ar trebui să **nu atingeți orice reușit** resurse (pentru că * - ar putea să nu fie acolo* mai), în timp ce încă eliberând resurse unmanaged. Modelul standard pentru a face acest lucru este de a avea
Finalize () " și " Dispose()ambele suna un *al treilea**(!) metoda; în cazul în care trece un Boolean spune dacă te - 're de asteptare de la Dispose()
(spre deosebire de Finalize()
), ceea ce înseamnă că's în condiții de siguranță pentru a elibera resurse gestionate.
Acest interne metoda - ar putea fi dat un nume arbitrar ca "CoreDispose", sau "MyInternalDispose", dar este tradiția de a numi Dispune(Boolean)
:
protected void Dispose(Boolean disposing)
Dar mai util parametru numele ar putea fi:
protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//Free managed resources too, but only if I'm being called from Dispose
//(If I'm being called from Finalize then the objects might not exist
//anymore
if (itIsSafeToAlsoFreeManagedObjects)
{
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose();
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose();
this.frameBufferImage = null;
}
}
}
Și-ți schimbi punerea în aplicare a `IDisposable.Dispose () metoda pentru a:
public void Dispose()
{
Dispose(true); //I am calling you from Dispose, it's safe
}
și finalizer la:
~MyObject()
{
Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
}
Notă: Dacă obiectul coboară de la un obiect care implementează
Dispune
, atunci nu't uita pentru a apela lor *baza *** Dispune metodă atunci când prototipul Dispune:
public override void Dispose()
{
try
{
Dispose(true); //true: safe to free managed resources
}
finally
{
base.Dispose();
}
}
Dacă utilizatorul solicită Dispose()
pe obiect, apoi totul a fost curățat. Mai târziu, când colectorul de gunoi vine și solicită Finaliza, se va apela apoi Dispune
din nou.
Nu numai ca aceasta este o risipă, dar dacă obiectul are junk referințe la obiecte deja eliminate de la ultimul sunați pentru a Dispose()
, ai'll încercați să eliminați-le din nou!
Te'll observa în codul meu am fost atent pentru a elimina referințele la obiecte care nu'am dispus, așa că don't încercați să sunați Dispune
pe un gunoi de obiect de referință. Dar asta nu't opri un subtil bug de la insinuează.
Atunci când utilizatorul solicită Dispose()
: mâner CursorFileBitmapIconServiceHandle este distrus. Mai târziu, când colectorul de gunoi se execută, se va încerca să distrugă același mâner din nou.
protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy
...
}
Cum ai rezolva acest lucru este de a spune gunoier care nu't nevoie să deranjez finalizarea obiect – resursele sale au fost deja curățat, și nu mai este necesar. Puteți face acest lucru prin apelarea GC.SuppressFinalize () " în " Dispose()
metoda:
public void Dispose()
{
Dispose(true); //I am calling you from Dispose, it's safe
GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
}
Acum că utilizatorul a numit Dispose()
, avem:
Documentația pentru Obiect.Finaliza
spune:
Finalizarea metodă este folosită pentru a efectua operațiunile de curățire pe unmanaged resursele deținute de către obiectul curent înainte de obiectul este distrus. Dar documentația MSDN, de asemenea, spune că, pentru [
IDisposable.Dispose
](https://msdn.microsoft.com/en-us/library/system.idisposable.dispose(v=vs. 110).aspx): Efectuează definite de aplicație sarcini asociate cu eliberarea, eliberarea, sau resetarea unmanaged resurse. Deci, ce este? Care este locul potrivit pentru mine pentru curatare unmanaged resurse? Răspunsul este: It'e alegerea ta! Dar alegeDispune
. Cu siguranță te-ar putea plasa unmanaged curățire în finalizer:
~MyObject()
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//A C# destructor automatically calls the destructor of its base class.
}
Problema e că nu ai nici o idee atunci când colectorul de gunoi va obține în jurul valorii de la finalizarea obiectului. Ne-a reușit, ne-necesare, ne-a utilizat resurse nativ se va lipi în jurul valorii de până când colectorul de gunoi în cele din urmă ruleaza. Atunci se va apela finalizer metodă; curățarea unmanaged resurse. Documentația de Obiect.A finaliza puncte de lucru:
timpul exact când finalizer execută este nedefinit. Pentru a asigura deterministe eliberarea de resurse pentru instanțe de clasă, să pună în aplicare un Aproape metoda sau să ofere o [
IDisposable.Dispune
][3] în aplicare. Aceasta este virtutea de a folosiDispune
de curățare unmanaged resurse; veți obține să știți, și de a controla, atunci când unmanaged resurse sunt curățate. Distrugerea lor este "deterministe".Să-ți răspund la întrebarea inițială: de Ce nu eliberați memorie acum, mai degrabă decât pentru atunci când GC decide să o facă? Am un software de recunoaștere facială care trebuie să scape de 530 MB de imagini interne acum, deoarece acestea're nu mai este necesar. Când ne-am don't: aparatul macină la un schimb de popas. Bonus De Lectură
Pentru oricine care îi place stilul de acest răspuns (explicarea de ce, deci cum devine evident), vă sugerez să citiți primul Capitol din Don Box's Esențială COM:
- Link Direct: Capitolul 1 proba de Editura Pearson
magnet: 84bf0b960936d677190a2be355858e80ef7542c0 În 35 de pagini, explică el problemele cu ajutorul obiectelor binare, și inventează COM fața ochilor tăi. Odată ce îți dai seama că de ce de COM, restul de 300 de pagini sunt evidente, și doar detaliu Microsoft's aplicare. Cred că fiecare programator care a avut de-a face cu obiecte sau COM ar trebui, cel puțin, citiți primul capitol. Aceasta este cea mai bună explicație a ceva vreodată. Bonus Suplimentar De Lectură
Când tot ce știu este greșit de Eric Lippert prin urmare, este foarte dificil într-adevăr să scrii corect finalizer, și cel mai bun sfat pot da este să nu încercați. [3]: https://msdn.microsoft.com/en-us/library/system.idisposable.dispose(v=vs. 110).aspx
IDisposable
este adesea folosit pentru a exploata "folosind" declarație și de a profita de o modalitate ușoară de a face deterministe de curatare de obiecte gestionate.
public class LoggingContext : IDisposable {
public Finicky(string name) {
Log.Write("Entering Log Context {0}", name);
Log.Indent();
}
public void Dispose() {
Log.Outdent();
}
public static void Main() {
Log.Write("Some initial stuff.");
try {
using(new LoggingContext()) {
Log.Write("Some stuff inside the context.");
throw new Exception();
}
} catch {
Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
} finally {
Log.Write("Some final stuff.");
}
}
}
Scopul Dispune model este de a oferi un mecanism pentru a curăța ambele administrate și neadministrate resurse și momentul în care are loc depinde de modul în care Dispune metoda se numește. În exemplu, utilizarea Dispune este, de fapt, nu fac nimic legat de a dispune, de compensare o listă nu are impactul pe care de colectare a fi eliminate. De asemenea, solicită să setați variabilele de nul, de asemenea, nu au niciun impact asupra GC.
Puteți lua o privire la acest lucru articolul pentru mai multe detalii cu privire la modul de a pune în aplicare Dispune model, dar practic se pare ca acest lucru:
public class SimpleCleanup : IDisposable
{
// some fields that require cleanup
private SafeHandle handle;
private bool disposed = false; // to detect redundant calls
public SimpleCleanup()
{
this.handle = /*...*/;
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources.
if (handle != null)
{
handle.Dispose();
}
}
// Dispose unmanaged managed resources.
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Metoda asta este cel mai important aici este de a Dispune(bool), care de fapt se execută în două circumstanțe diferite:
Problema cu pur și simplu lasă GC avea grijă de curățenie este că nu ai nici un control real asupra atunci când GC va rula un ciclu de colectare (puteți apela GC.Colecta(), dar nu ar trebui't) astfel încât resursele să pot rămâne mai mult timp decât este necesar. Amintiți-vă, de asteptare Dispose() nu't provoca de fapt, un ciclu de colectare sau în orice alt mod provoca GC pentru a colecta/free obiect; pur și simplu oferă mijloacele de a mai deterministicly curățire resursele utilizate și spune-GC că această curățire a fost deja efectuate.
Ideea de IDisposable și dispune model e't despre imediat eliberarea de memorie. Singura dată când un apel să Dispună de fapt va avea chiar și o șansă de imediat eliberarea de memorie este atunci când se ocupă de eliminare == false scenariu și manipularea unmanaged resurse. Pentru cod gestionat, memoria câștigat't de fapt să fie recuperat până la GC se execută un ciclu de colectare, care chiar nu ai nici un control asupra (altele decât de asteptare GC.Colecta(), pe care am'am menționat deja, nu este o idee bună).
Scenariul tău nu este't într-adevăr valabil deoarece in siruri de caractere .NET don't de a folosi orice unamanged resurse și don't pună în aplicare IDisposable, nu există nici o modalitate de a le forța să fie "curățat."
Nu ar trebui să existe în continuare apeluri de la un obiect's metode după Dispune a fost numit pe el (deși un obiect ar trebui să tolereze în continuare apeluri de a Dispune). Prin urmare, de exemplu, în întrebare este o prostie. Dacă Dispune este numit, apoi, obiectul în sine poate fi aruncată. Astfel, utilizatorul trebui să eliminați toate referirile la faptul că întregul obiect (setați-le la null) și toate legate de obiecte interne va primi în mod automat de curățat.
Ca pentru o întrebare generală despre reușit/unmanaged și discuții în alte răspunsuri, cred că orice răspuns la această întrebare trebuie să înceapă cu o definiție de o unmanaged de resurse.
Ceea ce se reduce la este că există o funcție, puteți apela pentru a pune sistemul într-o stare, și nu's-o altă funcție, puteți suna să-l aducă înapoi din acel stat. Acum, în tipic exemplu, primul ar putea fi o funcție care returnează un fișier mâner, și cel de-al doilea ar putea fi un apel la CloseHandle
.
Dar - și aceasta este cheia - ar putea fi orice pereche de funcții. Unul construiește un stat, altul l-a doborât. Dacă statul a fost construit, dar nu rupt în jos încă, atunci o instanță de resurse există. Va trebui să aranja pentru critică să se întâmple la momentul potrivit - resursa nu este gestionat de CLR. Singurul mod automat a reușit tip de resursă este de memorie. Există două tipuri: GC, și stivă. Tipuri de valoare sunt gestionate de stivă (sau cu autostopul în interiorul tipuri de referință), și tipuri de referință sunt gestionate de GC.
Aceste funcții pot provoca schimbări de stat care pot fi în mod liber intercalat, sau poate trebuie să fie perfect imbricate. Statul modificări pot fi sigură pentru fire, sau poate nu.
Uita-te la exemplu în Justiție's întrebare. Modificări la fișierul Jurnal's indentare trebuie să fie perfect imbricate, sau totul merge bine. De asemenea, acestea sunt puțin probabil să fie sigură pentru fire.
Este posibil să zgudui o plimbare cu garbage collector pentru a obține resurse unmanaged curățat. Dar numai dacă schimbarea statului de funcții sunt sigură pentru fire și două state pot avea vieti care se suprapun în nici un fel. Deci Dreptatea's de exemplu o resursă NU trebuie să aibă un finalizer! N't ajuta pe nimeni.
Pentru aceste tipuri de resurse, poți pune în aplicare IDisposable
, fără o finalizer. La finalizer este absolut optional - are să fie. Acest lucru este glosat de peste, sau nici măcar nu a menționat în mai multe cărți.
Apoi trebuie să utilizați "folosind" declarație pentru a avea vreo șansă de a se asigura că Dispune
este numit. Aceasta este, în esență, ca autostopul cu stiva (așa cum finalizer este la GC, "folosind" este de a stiva).
Partea lipsă este că trebuie să scrie manual Dispune și să-l sun pe domenii și clasa de baza. C++/CLI programatori don't trebuie să faci asta. Compilatorul scrie pentru ei, în cele mai multe cazuri.
Există o alternativă, pe care o prefer pentru statele care cuib perfect și nu sunt sigură pentru fire (în afară de orice altceva, evitând IDisposable piese de schimb de problema de a avea un argument cu cineva care poate't adauge o finalizer pentru fiecare clasă care implementează IDisposable).
În loc de a scrie o clasă, scrie o funcție. Funcția acceptă un delegat pentru a apela înapoi la:
public static void Indented(this Log log, Action action)
{
log.Indent();
try
{
action();
}
finally
{
log.Outdent();
}
}
Și apoi un exemplu simplu ar fi:
Log.Write("Message at the top");
Log.Indented(() =>
{
Log.Write("And this is indented");
Log.Indented(() =>
{
Log.Write("This is even more indented");
});
});
Log.Write("Back at the outermost level again");
Lambda fiind trecut în servește ca un bloc de cod, așa că's ca ai face-ți de propria structură de control pentru a servi aceluiași scop ca "folosind", cu excepția faptului că nu mai avea nici un pericol de a apelantului abuzeze de ea. Nu's nici un fel ei pot eșua pentru a curăța de resurse.
Această tehnică este mai puțin util în cazul în care resursa este la fel, care pot fi suprapuse vieți, pentru că atunci vrei să fii capabil de a construi O resursă, atunci resursă B, atunci ucide-O resursă și apoi mai târziu ucis de resurse B. puteți't face asta dacă te'am forțat utilizatorului de a perfect cuib de genul asta. Dar atunci ai nevoie pentru a folosi IDisposable` (dar încă fără un finalizer, cu excepția cazului în care ați implementat threadsafety, care e't gratuit).
Scenarii am face uz de IDisposable: curat unmanaged resurse, dezabonare pentru evenimente, legături strânse
Idiomul eu folosesc pentru punerea în aplicare a IDisposable (nu este sigură pentru fire):
class MyClass : IDisposable {
// ...
#region IDisposable Members and Helpers
private bool disposed = false;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing) {
if (!this.disposed) {
if (disposing) {
// cleanup code goes here
}
disposed = true;
}
}
~MyClass() {
Dispose(false);
}
#endregion
}
Da, codul este complet redundante și inutile și nu't face gunoier face nimic nu ar't face altfel (după ce o instanță de MyCollection iese din sfera de aplicare, care este.) Mai ales .Clar()
apeluri.
Răspunsul la edit: într-un Fel. Dacă am face acest lucru:
public void WasteMemory()
{
var instance = new MyCollection(); // this one has no Dispose() method
instance.FillItWithAMillionStrings();
}
// 1 million strings are in memory, but marked for reclamation by the GC
L's din punct de vedere funcțional identic cu acesta pentru scopuri de management al memoriei:
public void WasteMemory()
{
var instance = new MyCollection(); // this one has your Dispose()
instance.FillItWithAMillionStrings();
instance.Dispose();
}
// 1 million strings are in memory, but marked for reclamation by the GC
Dacă într-adevăr într-adevăr nevoie pentru a elibera memorie în acest moment, sun GC.Colecta()
. Nu's nici un motiv pentru a face acest lucru aici, totuși. Memoria va fi eliberat atunci când l's necesare.
Dacă MyCollection` va fi de gunoi colectate oricum, atunci nu ar trebui't nevoie de a dispune de ea. Acest lucru va doar putinei CPU mai mult decât este necesar, și poate chiar anula unele pre-calculate analiză care colectorul de gunoi a efectuat deja.
Eu folosesc IDisposable să faci lucruri cum ar fi asigurarea fire sunt eliminate corect, împreună cu unmanaged resurse.
EDIT ca răspuns la Scott's comentariu:
The numai timp GC indicatorii de performanță sunt afectați este atunci când un apel [sic] GC.Colecta() se face"
Din punct de vedere conceptual, GC menține o vedere de referință obiect grafic, și toate referirile la acesta din cadre stiva de fire. Rabla asta poate fi destul de mare și se întinde pe mai multe pagini de memorie. Ca o optimizare, GC cache analiza de pagini care sunt puțin probabil să se schimbe foarte des pentru a evita rescanarea pagina inutil. GC primește o notificare de la kernel-ul atunci când datele într-o pagină de schimbări, așa că știe că pagina este murdar și necesită o rescanare. Dacă colecția este în Gen0 apoi l's probabil că și alte lucruri în pagină se schimbă prea, dar acest lucru este puțin probabil în Gen1 și Gen2. Anecdotic, aceste carlige nu au fost disponibile în Mac OS X pentru echipa care a portat GC la Mac în scopul de a obține Silverlight plug-in de lucru pe platformă.
Un alt punct împotriva inutile dispoziția de resurse: imaginați-vă o situație în care un proces este în curs de descărcare. Imaginați-vă, de asemenea, că procesul a fost difuzate de ceva timp. Șansele sunt că multe dintre care procesul de's memorie pagini au fost schimbate pe disc. Cel puțin ei're nu mai este în L1 sau L2 cache. Într-o astfel de situație nu există nici un punct pentru o aplicație pe care o's descărcare pentru a schimba toate aceste date și pagini de cod înapoi în memorie pentru a 'de presă' resursele care vor fi lansate de către sistemul de operare, oricum, atunci când procesul se termină. Acest lucru se aplică reușit și chiar anumite resurse unmanaged. Numai resursele care tin non-fundal fire în viață trebuie să fie eliminate, în caz contrar procesul va rămâne în viață.
Acum, în timpul programului normal de execuție sunt efemere resurse care trebuie să fie curățate în mod corect (ca @fezmonkey subliniază database conexiuni, prize, fereastra handles) pentru a evita unmanaged pierderi de memorie. Acestea sunt tipurile de lucruri care trebuie să fie eliminate. Dacă vă creați un clasa care detine un fir (și prin detine vreau să spun că l-a creat și, prin urmare, este responsabil pentru asigurarea că se oprește, cel puțin de codificare meu stil), atunci acea clasă, cel mai probabil trebuie să pună în aplicare IDisposable și rupe în jos firul timpul
Dispune`.
A .NET framework folosește IDisposableinterfață ca un semnal de avertizare, pentru dezvoltatori, că această clasă _must_ fi eliminate. Pot't cred că de orice tipuri în cadru care pune în aplicare
IDisposable` (excl implementările de interfață explicită) în cazul în care dispoziția este opțională.
Daca vrei sa șterge acum, utilizați unmanaged de memorie.
A se vedea:
În exemplul postat de tine, încă nu't "liber de memorie acum". Memoria este de gunoi colectat, dar nu pot permite ca memoria să fie colectate într-o mai devreme generație. Te'd trebuie să fac niște teste pentru a fi sigur.
Cadrul Directoare de Proiectare sunt orientative, si nu reguli. Ei vă spun ce interfața este în primul rând pentru că, atunci când să-l folosească, cum să-l folosească, și când nu să-l folosească.
Am citit odată cod care a fost un simplu RollBack() în caz de eșec utilizarea IDisposable. La MiniTx clasa de mai jos ar verifica un steag pe Dispose() iar dacă Comit
apel nu s-a întâmplat atunci s-ar numi Revenire
pe sine. Se adaugă un strat de ocolire a face codul de asteptare mult mai ușor de înțeles și de întreținut. Rezultatul arata ceva de genul:
using( MiniTx tx = new MiniTx() )
{
// code that might not work.
tx.Commit();
}
Am'am văzut, de asemenea, de sincronizare / logare cod facă același lucru. În acest caz Dispose() metoda a oprit cronometrul și autentificat că blocul a ieșit.
using( LogTimer log = new LogTimer("MyCategory", "Some message") )
{
// code to time...
}
Deci, aici sunt câteva exemple concrete că don't face orice unmanaged resurse de curatare, dar folosit cu succes IDisposable pentru a crea cod curat.
Am câștigat't se repetă de obicei chestii despre Utilizarea sau eliberarea de onu resurse gestionate, care a fost acoperit. Dar aș dori să subliniez ceea ce pare a fi o concepție greșită comună.
Dat următorul cod
Public Class LargeStuff Implementează IDisposable Privat _Large ca șirul()Îmi dau seama că de Unică folosință în aplicare nu urmați orientările actuale, dar sper că toți ați înțeles ideea.'Un ciudat cod care înseamnă _Large conține acum mai multe milioane de șiruri lungi.
Sub Publice Dispose() Implementează IDisposable.Dispune _Large=Nimic End Sub
În afară de utilizarea sa principală ca o modalitate de a controla viață de resurse de sistem (complet acoperit de răspuns minunat de Ian, kudos!), la IDisposable/utilizarea combo poate fi, de asemenea, folosit pentru a domeniul de aplicare de schimbare de stare de (critice) resursele globale: a console, la fire, la proces orice obiect global ca o aplicarea exemplu.
Am'am scris un articol despre acest model: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/
Ea ilustrează cum vă puteți proteja unele adesea folosit stare globală într-un reutilizabile și ușor de citit mod: consola de culori, curent ață de cultura, aplicație Excel obiect proprietăți...
O problemă cu cele mai multe discuții de "unmanaged resurse" nu't într-adevăr defini termenul, dar par să sugereze că are ceva de-a face cu unmanaged code. În timp ce este adevărat că multe tipuri de resurse unmanaged face interfața cu unmanaged code, gândire de resurse unmanaged în astfel de termeni e't de ajutor.
În schimb, ar trebui să recunoască ce resurse gestionate în comun: toate acestea implică un obiect cere ceva în afara 'lucru' pentru a face ceva în numele său, în detrimentul unor alte 'lucruri', și alte entități de acord să facă acest lucru până la o notificare ulterioară. Dacă obiectul a fost de a fi abandonat și dispar fără urmă, nimic nu s-ar spune că, în afara 'lucru' că nu mai au nevoie să-și modifice comportamentul în numele obiectului care nu mai exista; prin urmare, 'lucru's utilitatea ar fi diminuat permanent.
O unmanaged resurse, atunci, reprezintă un acord de unii din afara 'lucru' să-și modifice comportamentul în numele unui obiect, care ar fi inutil să afecteze utilitatea că, în afara 'lucru' în cazul în care obiectul a fost abandonat și a încetat să mai existe. Un departament de resurse este un obiect care este beneficiar al unui astfel de acord, dar care s-a înscris pentru a primi o notificare dacă este abandonat, și care va folosi o astfel de notificare să-și pună afacerile în ordine înainte de a acesta este distrus.
Sunt lucruri pe care Dispose()
o operație în exemplul de cod care ar putea ** au un efect care nu ar apărea din cauza unei normal GC a MyCollection
obiect.
Dacă obiectele referite de _theList " sau "_theDict
sunt menționate de alte obiecte, atunci Lista<>" sau " Dicționar de<>` obiect nu vor fi supuse la colectie, dar va avea brusc conținutul nu. Dacă nu există Dispose() funcționarea ca în exemplu, aceste colecții va conține conținutul lor.
Desigur, dacă aceasta a fost situația că s-ar numi un rupt de proiectare - am'm doar subliniind (pedant, presupun) că Dispose()
operațiunea nu ar putea fi complet redundant, în funcție de faptul dacă există alte utilizări ale Listă<> " sau " Dicționar de<>` care nu sunt prezentate în fragment.
IDisposable
este bun pentru dezabonarea de la evenimente.
Mai întâi de definiție. Pentru mine unmanaged resource înseamnă o clasa care implementeaza interfata IDisposable sau ceva creat cu utilizarea de apeluri către dll. GC nu't știu cum să se ocupe cu astfel de obiecte. Dacă clasa are, de exemplu, numai tipuri de valoare, atunci nu't ia în considerare această clasă în clasa cu resurse unmanaged. Pentru codul meu am urmat următoarele practici:
<!
public class SomeClass : IDisposable
{
/// <summary>
/// As usually I don't care was object disposed or not
/// </summary>
public void SomeMethod()
{
if (_disposed)
throw new ObjectDisposedException("SomeClass instance been disposed");
}
public void Dispose()
{
Dispose(true);
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)//we are in the first call
{
}
_disposed = true;
}
}
[1]: https://msdn.microsoft.com/en-us/library/system.objectdisposedexception(v=vs. 110).aspx
Mai justificată caz de utilizare pentru eliminarea de resurse gestionate, este pregătirea pentru GC pentru a recupera resurse care altfel ar fi niciodată colectate.
Un prim exemplu este referințe circulare.
În timp ce-l's cele mai bune practici pentru a utiliza modele care evita referințe circulare, dacă se termină cu (de exemplu) o 'copil' obiect care are o trimitere înapoi la 'părinte', acest lucru poate opri GC colecție de mamă dacă ai abandona referință și se bazează pe GC - în plus, dacă aveți implementat un finalizer, l'nu va mai fi numit.
Singurul mod de a evita acest lucru este să manual rupe referințe circulare prin setarea Părinte trimiteri la nul pe copii.
De punere în aplicare IDisposable pe mamă și copii este cel mai bun mod de a face acest lucru. Atunci când se Dispune este numit pe Părintele, apel Dispune pe toți Copiii, și în care copilul Dispune metoda, setați Părinte trimiteri la null.
Dat exemplu de cod nu este un exemplu bun pentru IDisposable de utilizare. Dicționar de compensare normally ar trebui't du-te la Dispune
metoda. Dicționar elemente vor fi șterse și eliminate atunci când merge în afara domeniului de aplicare. `IDisposable punerea în aplicare este necesară pentru a elibera memorie/stivuitoare că nu va elibera/gratuit chiar și după ce au ieșit de aplicare.
Următorul exemplu arată un exemplu bun pentru IDisposable model cu un cod și comentarii.
public class DisposeExample
{
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class MyResource: IDisposable
{
// Pointer to an external unmanaged resource.
private IntPtr handle;
// Other managed resource this class uses.
private Component component = new Component();
// Track whether Dispose has been called.
private bool disposed = false;
// The class constructor.
public MyResource(IntPtr handle)
{
this.handle = handle;
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
component.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
CloseHandle(handle);
handle = IntPtr.Zero;
// Note disposing has been done.
disposed = true;
}
}
// Use interop to call the method necessary
// to clean up the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~MyResource()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}
public static void Main()
{
// Insert code here to create
// and use the MyResource object.
}
}
Am vedea o mulțime de răspunsuri au deplasat să vorbim despre utilizarea IDisposable pentru ambele administrate și neadministrate resurse. Am'd sugera acest articol ca fiind unul dintre cele mai bune explicațiile pe care le'am gasit pentru cum IDisposable ar trebui să fie efectiv utilizate.
https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About
Pentru întrebarea reală; ar trebui să utilizați IDisposable pentru a curăța reușit obiecte care sunt de a lua o mulțime de memorie răspunsul scurt ar fi nu. Motivul este că, odată ce Dispune de un IDisposable ar trebui să fie lăsat în afara domeniului de aplicare. În acel moment orice referire copilului obiecte sunt, de asemenea, în afara domeniului de aplicare și vor fi colectate.
Singura excepție de la acest lucru ar fi dacă ai avea o mulțime de memorie legat în obiecte gestionate și te'am blocat acel fir de așteptare pentru o operațiune pentru a finaliza. Dacă aceste obiecte în cazul în care nu va fi nevoie după acest apel finalizat setarea aceste trimiteri la nul ar putea permite colectorul de gunoi pentru a le colecta cât mai repede. Dar acest scenariu ar reprezenta cod rău că trebuia să fie refactored - nu un caz de utilizare a IDisposable.