{"id":85,"date":"2010-02-20T20:12:43","date_gmt":"2010-02-20T19:12:43","guid":{"rendered":"http:\/\/www.steffen-liersch.de\/content\/de\/?p=85"},"modified":"2025-03-02T11:32:14","modified_gmt":"2025-03-02T10:32:14","slug":"net-dispose-pattern","status":"publish","type":"post","link":"https:\/\/www.steffen-liersch.de\/content\/de\/2010\/02\/net-dispose-pattern\/","title":{"rendered":"Das Dispose-Pattern zur Ressourcenfreigabe in .NET"},"content":{"rendered":"<p>Das .NET Framework unterscheidet zwischen Referenztypen, die immer auf dem Heap organisiert werden, und den (normalerweise) auf dem Stack abgelegten Wertetypen. Heap-Objekte werden automatisch durch den Garbage Collector freigegeben.<!--more--><\/p>\n<p>Benutzt eine Klasse beispielsweise unverwaltete Ressourcen, ist die alleinige Freigabe dieser Ressourcen durch den Destruktor ungeeignet, da dieser ausschlie\u00dflich implizit vom Garbage Collector aufgerufen wird.<\/p>\n<p>Durch das Dispose-Pattern wurde eine M\u00f6glichkeit geschaffen, Ressourcen explizit und damit kontrolliert freizugeben.<\/p>\n<h2>Einsatzgebiete des Dispose-Patterns<\/h2>\n<p>Das Dispose-Pattern muss f\u00fcr Klassen implementiert werden, wenn mindestens eine der folgenden Bedingungen erf\u00fcllt ist:<\/p>\n<ul>\n<li>Klasse ist Eigent\u00fcmer von Objekten, die ebenfalls das Dispose-Pattern implementieren<\/li>\n<li>Klasse ist Eigent\u00fcmer von Ressourcen<\/li>\n<\/ul>\n<p>Das Dispose-Pattern sollte bereits in Basisklassen vorgesehen werden, wenn absehbar ist, dass es f\u00fcr abgeleitete Klassen ben\u00f6tigt werden k\u00f6nnte.<\/p>\n<h2>Aufbau des Dispose-Patterns<\/h2>\n<p>Der Hauptbestandteil des Dispose-Patterns ist die Funktion Dispose, durch die die Schnittstelle IDisposable implementiert wird.\u00a0Sie ist f\u00fcr die Ressourcenfreigabe verantwortlich.<\/p>\n<p>Im folgenden Beispiel wird das Dispose-Pattern f\u00fcr eine abstrakte Basisklasse verwendet. Die Funktion f\u00fcr die eigentliche Freigabe ist abstrakt und muss von den abgeleiteten Klassen bereitgestellt werden.<\/p>\n<pre><code class=\"cs\">using System;\r\n\r\nnamespace Liersch.DisposableExamples\r\n{\r\n  public abstract class DisposableObject : IDisposable\r\n  {\r\n    ~DisposableObject()\r\n    {\r\n      Dispose(false);\r\n    }\r\n\r\n    public void Dispose()\r\n    {\r\n      GC.SuppressFinalize(this);\r\n      Dispose(true);\r\n    }\r\n\r\n    protected abstract void Dispose(bool disposing);\r\n  }\r\n}<\/code><\/pre>\n<p>F\u00fcr den Fall, dass keine explizite Freigabe stattfindet, muss die Funktion Dispose vom Destruktor aufgerufen werden.<\/p>\n<p>SuppressFinalize kennzeichnet ein Objekt als freigegeben, so dass kein Destruktor-Aufruf w\u00e4hrend der Garbage Collection mehr erfolgt. Diese Kennzeichnung erfolgt vor der Freigabe durch die abstrakte Funktion. Dadurch wird sichergestellt, dass im Fehlerfall kein weiterer Aufruf mehr erfolgt.<\/p>\n<p>Wertetypen k\u00f6nnen zwar die Schnittstelle IDisposable implementieren, jedoch sind keine Destruktoren verf\u00fcgbar, so dass das Dispose-Pattern nicht vollst\u00e4ndig umsetzbar ist.<\/p>\n<h2>Explizite und implizite Ressourcenfreigabe<\/h2>\n<p>F\u00fcr Objekte, die das Dispose-Pattern implementieren, sollte immer eine explizite Freigabe erfolgen, sobald sie nicht mehr ben\u00f6tigt werden. Andernfalls erfolgt die Freigabe durch den Garbage Collector zu einem nicht vorhersehbaren Zeitpunkt, in nicht vorhersehbarer Reihenfolge und im Kontext eines beliebigen Threads.<\/p>\n<p>Durch eine explizite Freigabe kann erreicht werden, dass Ressourcen nur solange belegt bleiben, wie unbedingt n\u00f6tig. Au\u00dferdem kann eine kontrollierte Freigabe erfolgen. Das hei\u00dft, dass Objekte, die Eigent\u00fcmer anderer freizugebender Objekte sind, diese in einer bestimmten Reihenfolge freigeben k\u00f6nnen.<\/p>\n<p>Nicht-verwaltete Ressourcen m\u00fcssen unabh\u00e4ngig davon, ob es sich um einen expliziten oder impliziten Aufruf der Funktion Dispose handelt, immer freigegeben werden. Verwaltete Ressourcen hingegen d\u00fcrfen nur durch explizite Aufrufe der Funktion Dispose freigegeben werden. Andernfalls verursacht der implizite Aufruf von Dispose durch den Garbage Collector Fehler, da referenzierte Objekte bereits freigegeben worden sein k\u00f6nnten oder gerade durch einen anderen Thread freigegeben werden. Probleme treten dann h\u00e4ufig beim Beenden der Anwendung auf, da zu diesem Zeitpunkt die letzte Garbage Collection stattfindet.<\/p>\n<h2>Endg\u00fcltigkeit der Ressourcenfreigabe<\/h2>\n<p>Der Aufruf der Funktion SuppressFinalize stellt sicher, dass f\u00fcr ein explizit freigegebenes Objekt kein Aufruf des Destruktors mehr erfolgt, wenn das Objekt durch den Garbage Collector endg\u00fcltig entfernt wird. Die Garbage Collection wird dadurch beschleunigt.<\/p>\n<p>Freigegebene Objekte d\u00fcrfen nicht mehr benutzt werden. Funktionen, die ein bereits freigegebenes Objekt reaktivieren, sind ebenfalls nicht erlaubt, da eine implizite Freigabe aufgrund des bereits erfolgten Aufrufs von SuppressFinalize nicht mehr erfolgen w\u00fcrde.<\/p>\n<h2>Ressourcenbelegung im Konstruktor<\/h2>\n<p>Die Belegung von Ressourcen innerhalb des Konstruktors birgt einige Risiken, besonders dann, wenn mehrere Ressourcen ben\u00f6tigt werden. Schl\u00e4gt beispielsweise die zweite Anforderung fehl, erfolgt f\u00fcr die erste angeforderte Ressource keine explizite Freigabe, wenn keine Vorkehrungen in Form einer Ausnahmenbehandlung getroffen wurden.<\/p>\n<p>Die folgende Klasse startet im Konstruktor einen weiteren Thread zur Erledigung einer Aufgabe im Hintergrund. Ein manuelles Ereignis dient\u00a0zur Steuerung des Threads. Alle im Konstruktor angeforderten Ressourcen werden im Fehlerfall explizit freigegeben.<\/p>\n<pre><code class=\"cs\">using System;\r\nusing System.Diagnostics;\r\nusing System.Threading;\r\n\r\nnamespace Liersch.DisposableExamples\r\n{\r\n  public sealed class ExampleThread : DisposableObject\r\n  {\r\n    public ExampleThread()\r\n    {\r\n      try\r\n      {\r\n        m_Event=new ManualResetEvent(false);\r\n        m_Thread=new Thread(ThreadFunction);\r\n        m_Thread.IsBackground=true;\r\n        m_Thread.Start();\r\n      }\r\n      catch\r\n      {\r\n        Dispose();\r\n        throw; \/\/ Rethrow exception\r\n      }\r\n    }\r\n\r\n    protected override void Dispose(bool disposing)\r\n    {\r\n      if(disposing)\r\n      {\r\n        \/\/ Indicate thread termination request\r\n        m_Terminated=true;\r\n\r\n        \/\/ Stop waiting to get a fast feedback\r\n        if(m_Event!=null)\r\n          m_Event.Set();\r\n\r\n        \/\/ Wait for end of thread\r\n        if(m_Thread!=null &amp;&amp; m_Thread.IsAlive)\r\n          m_Thread.Join(); \/\/ Don't specify a time-out value here\r\n\r\n        \/\/ Release resources\r\n        if(m_Event!=null)\r\n          m_Event.Close();\r\n      }\r\n\r\n      m_Thread=null;\r\n      m_Event=null;\r\n    }\r\n\r\n    void ThreadFunction()\r\n    {\r\n      try\r\n      {\r\n        while(!m_Terminated)\r\n        {\r\n          \/\/ Wait a second\r\n          m_Event.WaitOne(1000);\r\n\r\n          \/\/ Thread termination requested?\r\n          if(m_Terminated)\r\n            break;\r\n\r\n          \/\/ Do something each second\r\n          DoSomething();\r\n        }\r\n      }\r\n      catch(Exception e)\r\n      {\r\n        Debug.Fail(\"Thread exception\", e.GetType().FullName+\" - \"+e.Message);\r\n      }\r\n    }\r\n\r\n    void DoSomething()\r\n    {\r\n      \/\/ ...\r\n\r\n      if(m_Terminated)\r\n        return;\r\n\r\n      \/\/ ...\r\n\r\n      if(m_Terminated)\r\n        return;\r\n\r\n      \/\/ ...\r\n\r\n      Debug.WriteLine(\"Thread example task done\");\r\n    }\r\n\r\n    bool m_Terminated;\r\n    ManualResetEvent m_Event;\r\n    Thread m_Thread;\r\n  }\r\n}<\/code><\/pre>\n<p>Wird von einer derartigen Klasse abgeleitet, muss eine analoge Ausnahmenbehandlung f\u00fcr alle Konstruktoren der neuen Klasse realisiert werden. Die spezielle Fehlerbehandlung f\u00fcr Konstruktoren entf\u00e4llt, wenn keine oder lediglich eine einfache Ressourcenbelegung erfolgt, also danach keine Aktionen folgen, die fehlschlagen k\u00f6nnten.<\/p>\n<h2>Zusammenfassung des Dispose-Patterns<\/h2>\n<ul>\n<li>Ressourcenbelegung m\u00f6glichst nicht im Konstruktor<\/li>\n<li>andernfalls Besonderheiten bei Ressourcenbelegung durch Konstruktor beachten<\/li>\n<li>m\u00f6glichst keine Ausnahmen innerhalb der Funktion Dispose verursachen<\/li>\n<li>Objekte nach dem Aufruf von Dispose nicht mehr verwenden<\/li>\n<li>Dispose-Pattern nach M\u00f6glichkeit nicht zu abgeleiteten Klassen hinzuf\u00fcgen<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Das .NET Framework unterscheidet zwischen Referenztypen, die immer auf dem Heap organisiert werden, und den (normalerweise) auf dem Stack abgelegten Wertetypen. Heap-Objekte werden automatisch durch den Garbage Collector freigegeben.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[21,25,19],"class_list":["post-85","post","type-post","status-publish","format-standard","hentry","category-net","tag-net","tag-cs","tag-basics"],"_links":{"self":[{"href":"https:\/\/www.steffen-liersch.de\/content\/de\/wp-json\/wp\/v2\/posts\/85","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.steffen-liersch.de\/content\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.steffen-liersch.de\/content\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.steffen-liersch.de\/content\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.steffen-liersch.de\/content\/de\/wp-json\/wp\/v2\/comments?post=85"}],"version-history":[{"count":17,"href":"https:\/\/www.steffen-liersch.de\/content\/de\/wp-json\/wp\/v2\/posts\/85\/revisions"}],"predecessor-version":[{"id":409,"href":"https:\/\/www.steffen-liersch.de\/content\/de\/wp-json\/wp\/v2\/posts\/85\/revisions\/409"}],"wp:attachment":[{"href":"https:\/\/www.steffen-liersch.de\/content\/de\/wp-json\/wp\/v2\/media?parent=85"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.steffen-liersch.de\/content\/de\/wp-json\/wp\/v2\/categories?post=85"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.steffen-liersch.de\/content\/de\/wp-json\/wp\/v2\/tags?post=85"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}