Vraag Waar te bellen Dispose () van IDisposable gemaakt in constructor?


Waar te bellen Dispose() voor IDisposable objecten die eigendom zijn van een object?

public class MyClass
{
    public MyClass()
    {
        log = new EventLog { Source = "MyLogSource", Log = "MyLog" };
        FileStream stream = File.Open("MyFile.txt", FileMode.OpenOrCreate);
    }


    private readonly EventLog log;
    private readonly FileStream stream;

    // Other members, using the fields above
}

Moet ik het implementeren Finalize() voor dit voorbeeld? Wat als ik helemaal niets implementeer? Zullen er problemen zijn?

Mijn eerste gedachte was dat MyClass zou moeten implementeren IDisposable. Maar de volgende verklaring in een MSDN-artikel verwarde me:

Implementeer IDisposable alleen als u onbeheerde resources direct gebruikt. Als uw app eenvoudig een object gebruikt dat wordt geïmplementeerd   IDisposable, geef geen IDisposable implementatie.

Is deze uitspraak onjuist?


13
2017-09-29 10:33


oorsprong


antwoorden:


Als MyClass  bezit een IDisposable bron MyClass zou zelf moeten zijn IDisposable, en het moet de ingekapselde bron verwijderen wanneer Dispose() wordt aangeroepen MyClass:

public class MyClass : IDisposable {
    // ...
    public virtual void Dispose() {
        if(stream != null) {
            stream.Dispose();
            stream = null;
        }
        if(log != null) {
            log.Dispose();
            log = null;
        }
    }
}

Nee, je zou hier geen finalizer moeten implementeren.

Opmerking: een alternatieve implementatie kan ongeveer zoiets zijn als:

private static void Dispose<T>(ref T obj) where T : class, IDisposable {
    if(obj != null) {
        try { obj.Dispose(); } catch {}
        obj = null;
    }
}

public virtual void Dispose() {
    Dispose(ref stream);
    Dispose(ref log);
}

25
2017-09-29 10:35



Voor objecten die andere bevatten IDisposable voorwerpen, het is een goede en aanbevolen praktijk om te implementeren IDisposable op uw eigen doel, zodat anderen die je type consumeren het in a kunnen verpakken using uitspraak:

public class MyClass : IDisposable
{
    public MyClass()
    {
        log = new EventLog { Source = "MyLogSource", Log="MyLog" };
        FileStream stream = File.Open("MyFile.txt", FileMode.OpenOrCreate);
    }


    private readonly EventLog log;
    private readonly FileStream stream;

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

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Free managed objects here
            stream.Dispose();
        }
    }

    // Other members, using the fields above
}

In jouw geval bevrijd je geen beheerde bronnen, dus er is geen finalizer nodig. als jij waren, dan zou je een finalizer implementeren en bellen Dispose(false), die aangeeft dat je de methode weggooit die wordt uitgevoerd vanaf de laatste thread.

Als u niet implementeert IDisposable, u laat het over aan de GC om de resources op te schonen (sluit bijvoorbeeld de Handle op de FileStream je hebt geopend) zodra het voor verzameling begint. Laten we je zeggen MyClass object komt in aanmerking voor verzameling en is momenteel in generatie 1. U verlaat uw FileStream handel open totdat de GC de resource opschoont als deze eenmaal is uitgevoerd. Ook veel implementaties van Dispose telefoontje GC.SuppressFinalize om te voorkomen dat uw object een andere GC-cyclus beleeft, van de Initialization Queue naar de F-bereikbare wachtrij.


1
2017-09-29 10:36



Veel van het advies rondom Dispose en Finalize werd geschreven door mensen die het verwachtten Finalize om werkbaar te zijn als mechanisme voor opschonen van primaire middelen. De ervaring heeft aangetoond dat die verwachting te optimistisch is geweest. Openbaar gerichte objecten die elke soort middelen verwerven en vasthouden tussen methodeaanroepen, moeten worden geïmplementeerd IDisposable en zou moeten niet override Finalize. Als een object bronnen bevat die anders niet zouden worden opgeruimd als het object zou worden verwijderd, zou het elke bron moeten inkapselen in een prive gehouden object dat dan zou moeten gebruiken Finalize om dat middel op te ruimen indien nodig.

Merk op dat een klasse over het algemeen niet zou moeten gebruiken Finalize om bronnen schoon te maken die in het bezit zijn van andere objecten. Als Finalize draait op een object dat een verwijzing naar het andere object bevat, meestal is een van de volgende voorwaarden van toepassing:

  • Er is geen andere verwijzing naar het andere object en deze is al uitgevoerd Finalize, dus dit object hoeft niets te doen om het schoon te maken.
  • Er is geen andere verwijzing naar het andere object en deze is nog niet uitgevoerd Finalize maar is gepland om dit te doen, dus dit object hoeft niets te doen om het op te ruimen.
  • Iets anders gebruikt nog steeds het andere object, dus dit object moet niet proberen het op te ruimen.
  • De opschoonmethode van het andere object kan niet veilig worden uitgevoerd vanuit de context van een finalisator-thread, dus dit object zou niet moeten proberen het op te schonen.
  • Dit object kwam alleen in aanmerking om te worden uitgevoerd Finalize omdat alle noodzakelijke opruiming al is voltooid, dus dit object hoeft niets te doen om dingen op te ruimen.

Definieer alleen een Finalize methode in die gevallen waarin men kan begrijpen waarom geen van de bovenstaande voorwaarden van toepassing is. Hoewel dergelijke gevallen bestaan, zijn ze zeldzaam en is het beter om er geen te hebben Finalize methode dan om een ​​ongepaste te hebben.


1
2017-09-29 18:25