Destruktor unter c#



  • wiso wird der destruktor unter c# nicht korrekt aufgerufen?
    habe eine klasse, bei der ich zum schluss ein paar sachen vornehmen muss. habe dies in den destruktor gepackt, aber der wird nicht aufgerufen.

    im netz finde ich auch keine brauchbaren infos. nur wie man den destruktor implementiert, vierl über die finalize funktion, aber nicht wiso der desturktor nicht aufgerufen wird.
    der sollte doch eigentlich automatisch über den GC aufgerufen werden, weil es kein delete mehr gibt?





  • Gerdi schrieb:

    wiso wird der destruktor unter c# nicht korrekt aufgerufen?

    Wird er. Nur nicht dann, wenn Du das erwartest. Man findet im Netz übrigens jede Menge brauchbarer Infos, die sind allerdings etwas schwer zu sichten. Daher ein kurzer Überblick:

    .NET verwendet einen nicht-deterministischen Garbage Collector. Speicher wird nicht dann freigegeben, wenn er nicht mehr benötigt wird, sondern erst dann, wenn neuer Speicher benötigt wird. Das führt dazu, dass ein Objekt auch noch lange existieren kann, nachdem Du keinen Verweis mehr darauf führst.

    Für den Destruktor hat dies die Konsequenz, dass er irgendwann ausgeführt wird – und meistens zu einem Zeitpunkt, zu dem er keine sinnvolle Arbeit mehr verrichten kann.

    Daher: Vergiss den Destruktor, er ist nicht mehr für Aufräumarbeiten geeignet.

    habe eine klasse, bei der ich zum schluss ein paar sachen vornehmen muss.

    Für sowas benutzt man unter .NET das sogenannte IDisposable-Pattern (=> Suchmaschine!). IDisposable ist eine Schnittstelle, die die eine Methode 'Dispose' implementiert, in der Aufräumarbeiten erledigt werden können. Wenn Du Objekte verwendest, die diese Schnittstelle implementieren, solltest Du nach ihrer Benutzung unbedingt 'Dispose' aufrufen.

    Da man das leicht vergisst (oder weil eine Exception dazwischen kommen kann), gibt es in C# und VB ein besonderes Sprachkonstrukt, das den Aufruf von 'Dispose' garantiert:

    // Erstellt ein
    using (MeineKlasse X = new MeineKlasse()) {
        // Hier kann man X benutzen
    }
    

    Der obige Block erstellt ein Objekt 'X' vom Typ 'MeineKlasse'. In dem Block kann man mit 'X' arbeiten, aber am Ende des Blocks wird das Objekt automatisch 'Dispose'd. Der Code ist also identisch zu folgendem Code:

    {
        MeineKlasse X;
        try {
            X = new MeineKlasse();
            // Hier kann man X benutzen
        }
        finally {
            if (X != null)
                X.Dispose();
        }
    }
    

    HTH.



  • hm, ich verstehe dann nicht den sinn und zweck des (.Net-) GC weil:
    der destruktor wird definitiv nicht automatisch in meinem Web-Projekt aufgerufen.
    D.h. ich muss manuell die Dispose-Funktion aufrufen. Ob ich jetzt delete myObj; oder myObj.Dispose(); schreibe ist eigentlich Jacke wie Hose. Mit delete hab ich sogar noch mehr Einfluss drauf, wann der Speicher freigegeben wird etc.. Ausserdem hab ich mehr overhead, weil ich von der Funktion IDisposable ableiten muss.

    was hab ich übersehen(welchen wirklichen vorteil gibts es?)



  • Der Vorteil ist, dass der Speicher eines Objekts von GC verwaltet wird.
    Meistens haben die Objekte keine Resources, die explizit freigegeben werden müssen . Dies kann natürlich trotzdem vorkommen, desshalb das Dispose Pattern.

    Zusammenfassend:
    Mit dem GC hat man in den meisten Fällen nichts mit Resourcenfreigabe zu tun und wenn es trotzdem nötig sein sollte, dann kann das Dispose Pattern umgesetzt werden.



  • ok, ich danke euch
    habs verstanden



  • Gerdi schrieb:

    Ob ich jetzt delete myObj; oder myObj.Dispose(); schreibe ist eigentlich Jacke wie Hose. Mit delete hab ich sogar noch mehr Einfluss drauf, wann der Speicher freigegeben wird etc.. Ausserdem hab ich mehr overhead, weil ich von der Funktion IDisposable ableiten muss.

    Hier sprichst Du übrigens wichtige Punkte an.

    Klar, wie Simon sagte, hat der GC durchaus Vorteile. Aber die entkoppelte Ressourcenverwaltung kann man durchaus auch als Nachteil ansehen. Der GC ist halt ein Abwägen verschiedener Faktoren und durchaus nicht immer die beste Lösung.



  • Gerdi schrieb:

    D.h. ich muss manuell die Dispose-Funktion aufrufen. Ob ich jetzt delete myObj; oder myObj.Dispose(); schreibe ist eigentlich Jacke wie Hose. Mit delete hab ich sogar noch mehr Einfluss drauf, wann der Speicher freigegeben wird etc.. Ausserdem hab ich mehr overhead, weil ich von der Funktion IDisposable ableiten muss.

    was hab ich übersehen(welchen wirklichen vorteil gibts es?)

    Du hast nichts "übersehen" sondern bist zu undifferenziert was die Resourcenverwaltung angeht. Es gibt unterschiedliche Resourcetypen die je nah Typ eine andere Art der Resourcenverwaltung brauchen. Bei Resourcen wie z.B. Speicher hast Du einen Typ bei dem es vollkommen egal ist wann der Speicher freigegeben wird. Meistens ist es sogar besser man sammelt nicht mehr benötigte Objekte erstemal und räumt diese dann in einem Durchlauf auf, um so den Overhead der Speicehrverwaltung zu reduzieren. Unterm Strich kann eine gut eingestellte GC die Gesamtperformance des Programms deutlich verbessern.

    Hinzu kommt eine deutliche Entlastung des Programmierers weil der nicht mehr jeden Furz von Hand freigeben muß.

    Dem entgegen stehen Resourcen wie z.B. Dateien, bei denen es wichtig ist die Resource so schnell wie möglich wieder freizugeben sobald sie nicht mehr bebötigt werden. Für solche Resourcen ist eine GC natürlich der vollkommen falsche Weg. Für solche Resourcen ist das Dispose-Pattern gedacht.

    Das Net-Framework bietet hier die Möglichkeit in BEIDEN Fällen eine saubere Lösung zu haben. Für Resourcen vom ersten Typ kann man bedenkenlos der GC die Arbeit überlassen und bei knappen Resourcen baut man explizit eine Dispose-Funktion ein. Unterm Strich immer noch besser zu Verwalten weil man sich nur auf die "wichtigen" Resourcen konzentrieren muß.

    Als kleinen Bonus sorgt das IDisposeable-Pattern auch noch dafür das im Zweifelsfall eine resource, die man vergessen hat explizit freizugeben spätestens beim nächsten GC-Lauf freigegeben wird. Also ein zusätzliches Sicherheitsnetz.

    UND.

    Man kann so schöne Konstrukte wie:

    // pseudocode
    using( DateiResource x = OpenDatei())
    {
    }
    

    benutzen wo dann garantiert beim verlassen des Blocks die Dispose-Funktion aufgerufen wird

    Brauchst Du wirklich noch mehr Vorteile?



  • loks schrieb:

    Brauchst Du wirklich noch mehr Vorteile?

    Ich bevorzuge trotzdem RAII im C++-Stil. Man darf nicht vergessen, dass diese ganze 'using'-Sache erst mit .NET 2.0 dazugekomme ist, vorher gab es das nicht. Man hat aber gesehen, dass es einfach keinen sauberen Weg gab, (Nicht-Speicher-)Ressourcen freizugeben, jetzt, da man sie von der Speicherverwaltung entkoppelt hatte und hat daher das 'using' nachträglich eingeführt, um RAII doch wieder zu erlauben.

    Nun ist es aber so: Eine Nicht-Speicher-Ressource ist *immer* an eine Speicher-Ressource gebunden (anders kann man sie einfach nicht modellieren). Es ist daher durchaus sinnvoll, beide gleich zu behandeln, wie das bei RAII der Fall ist.

    Dein GC-Argument (Speicherfreigabe gemeinsam und nicht stückweise) trifft auch nur dann zu, wenn man es mit Objekten zu tun hat, die auf dem Heap lagern müssen und nicht auf dem Stack. Solange diese Frameworks aber über keine gut funktionierende Escape-Analyse verfügen (und soweit ich weiß ist das selbst in Java 6 noch eher Glückssache), ist es allemal effizienter, soviele Objekte wie möglich auf dem Stack abzulegen.

    Stroustrup hat es mal (stark vereinfacht) sehr schön gesagt: „C++ is the best garbage-collected language simply because it produces less garbage.“



  • Man darf nicht vergessen, dass diese ganze 'using'-Sache erst mit .NET 2.0 dazugekomme ist, vorher gab es das nicht.

    using gibts schon ab V1.1 ... ev. war es noch nicht in V1.0:
    http://msdn2.microsoft.com/en-us/library/yh598w02(VS.71).aspx

    Grüsse Simon



  • simon.gysi schrieb:

    Man darf nicht vergessen, dass diese ganze 'using'-Sache erst mit .NET 2.0 dazugekomme ist, vorher gab es das nicht.

    using gibts schon ab V1.1 ... ev. war es noch nicht in V1.0:
    http://msdn2.microsoft.com/en-us/library/yh598w02(VS.71).aspx

    Stimmt, hatte ich mit VB verwechselt (da kam es erst in Version 8). Ändert aber nichts an dem Gesagten.


Log in to reply