Allokation im Speicher von weitergegebenen Variablen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 55 Antworten in diesem Thema. Der letzte Beitrag () ist von Haudruferzappeltnoch.

    Haudruferzappeltnoch schrieb:

    Nagut mit Klassenvariable dispose ich dann nur noch am Ende.
    Wenn das eine Form oder ein UserControl ist, pack das Dispose in die FormX.Dispose(bool disp) in der FormX.Designer.vb.
    Wenn nicht, lass die Klasse IDisposable implementieren, wo Du Deine Variabnle dann disposen kannst.
    usw.
    Das Dispose der letzten Klasse sollte dann in der FormX.Dispose(..) passieren.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    petaod schrieb:

    Ich verstehe nicht, warum du unbedingt von Hand disposen willst.
    Da haben sich die Compilerentwickler sehr viele Gedanken gemacht, damit das automatisch geht.
    Das ist nicht richtig.
    Disposen geht nicht automatisch, und muss immer von Hand gemacht werden.

    Mir scheint, du verwechselst Disposen mit der Garbage-Collection, oder mit dem Finalizer.
    Weil auf den GC trifft zu was du sagst: der gibt automatisch SystemResourcen frei, die nicht mehr gebraucht werden.
    Und zwar äusserst effizient - und da soll man nichts von Hand machen.

    Aber es gibt SystemResourcen, die kann der GC nicht abräumen.
    Und für genau diese Dinge (und alle Klassen, die diese Dinge benutzen) ist der Dispose-Pattern und die IDisposable - Schnittstelle, und auch das Using-Schlüsselwort erfunden worden: Damit der Programmierer selbst bestimmt ("von Hand" also), wann genau ein Objekt nicht mehr gebraucht wird und aufgeräumt wird.

    ErfinderDesRades schrieb:

    Disposen geht nicht automatisch, und muss immer von Hand gemacht werden.
    Ein managed object wird automatisch disposed, sobald es out-of-scope geht.
    Es gibt ja genügend Klassen, die IDisposable gar nicht implementiert haben.
    Deren Instanzen kannst du gar nicht manuell disposen.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    tja, du liegst falsch.
    Such dir Buch oder gute Info im Internet (CodeProject-Artikel, MS-Doku) und bring dich auffn Stand.

    Du kannst auch ein Experiment machen: Öffne einen Filestream, lies zwei Zeichen aus, und lass ihn aus dem Scope gehen.
    Dann versuch dieselbe Datei mit Notepad zu öffnen, zu ändern, zu speichern. Wenn ich mich recht erinnere kriegst du dabei die Info, dass die Datei gesperrt ist.

    Die wird erst wieder frei, wenn du dein Programm schließt.

    petaod schrieb:


    Ein managed object wird automatisch disposed, sobald es out-of-scope geht.
    Es gibt ja genügend Klassen, die IDisposable gar nicht implementiert haben.
    Deren Instanzen kannst du gar nicht manuell disposen.


    Mal am Beispiel von docs.microsoft.com/de-de/dotne…o.filestream?view=net-6.0
    MS schreibt:
    Dieser Typ implementiert die IDisposable-Schnittstelle.
    Nach Abschluss der Verwendung sollten Sie den Typ entweder direkt oder
    indirekt löschen. Zum direkten Löschen des Typs rufen Sie seine Dispose-Methode in einem try/catch-Block auf. Zum indirekten Löschen verwenden Sie ein Sprachkonstrukt wie using (in C#) oder Using
    (in Visual Basic). Weitere Informationen finden Sie im Abschnitt
    „Verwenden eines Objekts, das IDisposable implementiert“ des Themas „Die
    IDisposable-Schnittstelle“.


    Umn Missverständnisse auszuschliessen, würde ich nicht "von Hand gemacht" schreiben / verwenden.
    Benutze ich das Schlüsselwort USING nicht, nützt mir die Impelementierung der IDisposable Schnittstelle auch nichts, wenn ich vergesse zu Disposen.
    Das ist, was @ErfinderDesRades meint, behaupte ich mal frech.
    Das Using-Schlüsselwort als indirekte Verwendung des Disposen zu bezeichnen finde ich da genauer. Ich "mach" es ja eben nicht von Hand, streng genommen.

    Dksksm schrieb:

    Ich "mach" es ja eben nicht von Hand, streng genommen.
    Das geht halt so:
    Allokation im Speicher von weitergegebenen Variablen
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hier nochmal ein anderes Beispiel wo eine Funktion ein IDisposable erzeugt und diese quasi gleich zweimal übergeben wird.

    VB.NET-Quellcode

    1. Sub Main
    2. Dim cmd = Sql.CreateCommand
    3. ...
    4. cmd.Dispose 'Hier wird cmd.Connection meines Wissens nach mit disposed
    5. End Sub
    6. Friend Class Sql
    7. ...
    8. Private Function CreateSqlConnection() As SqlConnection
    9. Return New SqlConnection(ConnString) 'Das hier wird ja nie Disposed
    10. End Function
    11. Friend Function CreateCommand() As SqlCommand
    12. Return New SqlCommand(SqlString, CreateSqlConnection()) 'Und der hier auch nicht, dann habe ich einen Command und die Connection schon zweimal übrig.
    13. End Function
    14. End Class

    Oder wie muss man sich das vorstellen?
    Die SqlConnection wird da m.E. nicht disposed. Das wär Aufgabe der Sql-Klasse, dies zu tun, indem nicht Return New ausgeführt wird, sondern der Variablenwert jeweils zwischengespeichert wird und dann Sql selber IDisposable implementiert und beide Variablen disposed. cmd.Dispose in Zeile#4 ist da ein wenig ungünstig, da könntest Du auch gleich probieren, über cmd an die Connection zu kommen und diese auch disposen, keine Ahnung, ob da der Zugriff besteht. Aber es ist eben nicht Aufgabe von Main, die Sql-Klasseninstanzbestandteile zu disposen, sondern Aufgabe der Sql-Klasseninstanz.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Ja moment. In der Sql-Klasse verstehe ich was du meinst.
    Aber in der Main wird ja Dim cmd deklariert, dann räumt Main cmd auch wieder auf, oder nicht? Da kann die Sql-Klasse auch gar nicht mehr dran rumfingern, wenn die Dispose aufruft, das ist ja eine lokale Variable.

    Ist SqlConnection/SqlCommand ein verwaltetes oder nicht verwaltetes Objekt? Ich habe angenommen ein verwaltetes, weil sonst weiß ich nicht wie man verwaltete Objekte bereinigt.

    VB.NET-Quellcode

    1. Sub Main
    2. Dim cmd = Sql.CreateCommand
    3. ...
    4. cmd.Connection.Dispose 'Sollte eigentlich in cmd.Dispose passieren, sonst wäre doch die Dispose Methode in SqlCommand falsch implementiert, der muss doch seine Properties aufräumen
    5. cmd.Dispose
    6. End Sub
    7. Private Sub Ende
    8. Sql.Dispose
    9. End Sub
    10. Friend Class Sql
    11. Implements IDisposable
    12. Private con as SqlConnection
    13. Private cmd as SqlCommand
    14. ...
    15. Private Sub CreateSqlConnection() As SqlConnection
    16. con = New SqlConnection(ConnString)
    17. Return con
    18. End Function
    19. Friend Function CreateCommand() As SqlCommand
    20. cmd = New SqlCommand(SqlString, CreateSqlConnection()) ' Jetzt ist die Funktion CreateSqlConnection ja eigentlich Quatsch
    21. Return cmd
    22. 'con = New SqlConnection(ConnString) 'Alternative ohne CreateSqlConnection
    23. 'cmd = New SqlCommand(SqlString, con)
    24. 'Return cmd
    25. End Function
    26. Protected Overridable Sub Dispose(disposing As Boolean)
    27. If Not disposedValue Then
    28. If disposing Then
    29. cmd.Dispose
    30. con.Dispose
    31. End If
    32. disposedValue = True
    33. End If
    34. End Sub
    35. Public Sub Dispose() Implements IDisposable.Dispose
    36. Dispose(disposing:=True)
    37. GC.SuppressFinalize(Me)
    38. End Sub
    39. End Class

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Haudruferzappeltnoch schrieb:

    dann räumt Main cmd auch wieder auf, oder nicht?
    Ja, aber Main ruft nicht die Dispose-Methode von cmd automatisch auf. Und schon gar nicht von der SqlConnection. Daher rufst Du cmd.Dispose auf, um zumindest cmd zu disposen. Aber das heißt nicht, dass die SqlConnection auch disposed wird. Die Dispose-Methode eines Objekts muss immer explizit aufgerufen werden oder implizit durch das Ende eines Using-Blocks. Beides ist hier nicht für die SqlConnection gegeben.
    Die Sql-Klasse soll also die SqlConnection-Instanz halten und am Ende disposen und auch den SqlCommand halten und disposen. Am konsequentesten, indem sie selbst IDisposable implementiert und in der Dispose-Methode bei beiden Unterklasseninstanzen die Dispose-Methode aufruft.
    Verwaltete Objekte haben keine Dispose-Methode. Wenn eine Klasse eine Dispose-Methode hat, muss die auch verwendet werden. Und das muss man selber tun. Daher sind solche keine verwalteten Objekte. Verwaltete Objekte haben keine Dispose-Methode und werden vom GC selbst entsorgt. In Post#29 ist deine Sql-Klasse eine verwaltete Klasse, da sie keine Dispose-Methode hat. Da sie aber selbst Klasseninstanzen verwendet, die je eine Dispose-Methode haben, somit also mit nicht-verwalteten Objektklassen arbeitet, muss die Sql-Klasse sich um deren Entsorgung kümmern, also am besten, indem sie - wie gesagt - diese selbst entsorgt. Das macht man mit der IDisposable-Implementierung, was aber dazu führt, dass Deine Sql-Klasse keine verwaltete Klasse mehr ist.

    So, und Dein Codevorschlag im Vorpost ist (zumindest beim ersten Überfliegen) fast genau richtig. Nur die Zeilen#4-#5 kannst Du weglassen, weil das Disposen der Unterklassenobjekte effektiv mit der Zeile#8 gemacht wird.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Ok die IDisposable Implementation sagt explizit im Code, wo die verwalteten Resourcen aufgeräumt werden sollen und wo die nicht-verwalteten.
    Ich hatte die Disposes (Post 31) an die verwaltete Stelle geschrieben, aber das ist ja schnell zu ändern.
    Aber das heißt dann soll ich da ja Objekte aufräumen, die kein IDisposable implementieren also z.B. eine String Property von Sql. Aber genau sowas ist ja eigentlich überflüssig. Es sei denn dadaurch das Sql jetzt nicht mehr verwaltet ist, sind auch die eigentlich verwalteten Properties nicht mehr verwaltet.
    In dem Fall setze ich dort dann einen String auf Empty oder auf Nothing?

    Den Rest verstehe ich nicht, wenn ich Zeile 4-5 weglasse, dann beendet Sub Main und die lokale Variable verschwindet im Off und ist nicht mehr ansprechbar, das heißt die bleibt sofort undisposed zurück.
    Wenn SqlCommand die Dispose Methode implementiert, sollte diese Dispose Methode ein Dispose für die SqlConnection Property des SqlCommand aufrufen, zumindest würde man das ja genauso programmieren, wenn man für die SqlCommand Klasse selbst IDisposable implementieren müsste.

    Und dann lese ich z.B. in der MS-Doku in einem Code-Beispiel " ' Dispose managed objects that implement IDisposable." Also sind Disposable Objekte nicht generell nicht verwaltet?

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Haudruferzappeltnoch schrieb:

    Also sind Disposable Objekte nicht generell nicht verwaltet?
    lies nochma vpzs post: als Managed werden wohl die bezeichnet, die IDisposable implementieren. Folglich als unmanaged jene, die IDisposable nicht implementieren.
    Wobei ich bei unmanaged Objects nicht recht weiss, warum ich die freigeben sollte - das macht doch der GC.

    C#-Quellcode

    1. GC.SuppressFinalize(this);
    sehe ich mit Unbehagen.
    Was, wenn mein Dispose die unmanaged Resourcen (was immer das sein mag) nicht korrekt und vollständig freigibt?
    (Das passiert doch ganz schnell - man hat vor Monaten Dispose implementiert, und nun macht man eine Erweiterung und vergisst, das Disposen nachzuführen)
    Dann hab ich ein MemoryLeak, weil der GC wird das jetzt auch nicht freigeben?

    ErfinderDesRades schrieb:

    als Managed werden wohl die bezeichnet, die IDisposable implementieren. Folglich als unmanaged jene, die IDisposable nicht implementieren.
    Hätte nicht erwartet, dass mein Post so falsch verstanden werden kann 8|
    Ich meinte es nämlich genau umgekehrt. Alles, was IDisposable implementiert, ist unmanaged, alles andere managed, weil vom GC komplett biologisch abbaubar.
    Aber so ist es wohl dann doch nicht. Managed heißt wohl: Ein in der CLR erstelltes Objekt und unmanaged außerhalb davon. Wenn man also irgendwelche WinAPI-Funktionen nutzt (die dann auf C++-Basis erstellt sind und solchen Code aufrufen), werden ggf. Objekte erstellt, die am Ende freigegeben werden müssen. Jene Objekte sind unmanaged. Werden aber nun in C#/VB.NET (CLR) Klassen erstellt, die diese unmanaged-Objekte nutzen, sollten jene CLR-Objekte IDisposable implementieren, um dann die unmanaged-Objekte artgerecht zu entsorgen, weil es der GC nicht kann.
    Es hat also doch nix mit Dispose und Co zu tun, sondern mit der Herkunft der entstehenden Objekte. Der GC kann sich nur um CLR-Objekte kümmern.

    Und bevor ich noch mehr Halbwissen verbreite: Bitte ein paar korrigierende Aussagen von Leuten, die sich wirklich auskennen :D
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Aber es ist ja auch wirklich nirgends erklärt. Jede ansatzweise MS-Erklärung geht vom hundertsten ins tausendste.
    Man muss wissen was CLR ist, damit man weiß was managed und unmanaged ist, damit man weiß, was der GC ist, damit man weiß was man beim Implementieren von Dispose macht, damit man Disposen kann, damit man keinen Müll stehen lässt, wenn man 20 Zeichen Code benutzt: Dim a = New SqlCommand()

    Ich habs jetzt also so, nach weiterer Überarbeitung:

    VB.NET-Quellcode

    1. Sub Main
    2. 'Nutzt nur noch Sql1.cmd, hat kein lokales SqlCommand mehr
    3. End Sub
    4. Private Sub Ende
    5. Sql1.Dispose
    6. End Sub
    7. Friend Class Sql
    8. Implements IDisposable
    9. Private con as SqlConnection
    10. Friend cmd as SqlCommand
    11. ...
    12. Friend Sub New()
    13. con = New SqlConnection(ConnString)
    14. cmd = New SqlCommand(SqlString, con)
    15. End Sub
    16. Protected Overridable Sub Dispose(disposing As Boolean)
    17. If Not disposedValue Then
    18. If disposing Then
    19. cmd.Dispose
    20. con.Dispose
    21. End If
    22. disposedValue = True
    23. End If
    24. End Sub
    25. Public Sub Dispose() Implements IDisposable.Dispose
    26. Dispose(disposing:=True)
    27. GC.SuppressFinalize(Me)
    28. End Sub
    29. End Class

    Aus cmd eine Property zu machen, hab ich mich nicht getraut. Dann habe ich ja cmd und das dazugehörige Feld _cmd. Wer weiß was da wieder schief gehen kann.
    GC.SuppressFinalize wird so vom Vorschlage-Code bei der IDisposable Implementation erzeugt. Und es steht extra dabei als Kommentar diese argumentfreie Dispose-Methode nicht zu ändern

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    VaporiZed schrieb:

    Halbwissen ... Bitte ein paar korrigierende Aussagen von Leuten, die sich wirklich auskennen
    hahaha - der war gut!
    Mir gehts wie Haudruff - ich hab im INet noch nix gefunden, wo ich dachte: "Ah, der kennt sich aus, und kanns erklären in dem Sinne, dasses hinterher klar ist."

    Haudruferzappeltnoch schrieb:

    Und es steht extra dabei als Kommentar diese argumentfreie Dispose-Methode nicht zu ändern
    Wie gesagt: Mich gruselt das total, den GC zu umgehen - da kann MS sonstwas in seinen generierten Code schreiben.

    Aber mal andere Frage: Wieso gibts bei dir ein OverRidable Sub Dispose(bool)? Hast du vor, deine Klasse zu beerben?
    Das sind so die Sachen von MS - die tun gerne mal zuviel des Guten, und beim Proggen gilt "Zuviel des Guten ist ungut, höchst ungut" - (ein interessante Paradoxon eiglich).
    Ja ich habs mich auch schon gefragt. Dispose(True) ist quasi Dispose() nur + eigenes Aufräumen, und Dispose(False) braucht man nur für Erben. Wo da jetzt genau weiß ich auch nicht.

    Also du würdest es so machen?

    VB.NET-Quellcode

    1. Sub Main
    2. 'Nutzt nur noch Sql1.cmd, hat kein lokales SqlCommand mehr
    3. End Sub
    4. Private Sub Ende
    5. Sql1.Dispose
    6. End Sub
    7. Friend Class Sql
    8. Implements IDisposable
    9. Private con as SqlConnection
    10. Friend cmd as SqlCommand
    11. ...
    12. Friend Sub New()
    13. con = New SqlConnection(ConnString)
    14. cmd = New SqlCommand(SqlString, con)
    15. End Sub
    16. Public Sub Dispose() Implements IDisposable.Dispose
    17. If Not disposedValue Then
    18. cmd.Dispose
    19. con.Dispose
    20. disposedValue = True
    21. End If
    22. End Sub
    23. End Class

    Im Zweifel rutscht der GC nochmal über eine aufgeräumte Sql Instanz, was wahrscheinlich nicht schlimm ist

    Aber was ich eigentlich noch wissen wollte, was ist wenn ich hier

    VB.NET-Quellcode

    1. Friend Class Sql
    2. Private con as SqlConnection
    3. Friend cmd as SqlCommand
    4. ...
    5. Friend Sub New()
    6. con = New SqlConnection(ConnString)
    7. End Sub
    8. Friend Function CreateSqlCommand() as SqlCommand
    9. cmd = New SqlCommand(SqlString, con)
    10. Return cmd
    11. End Function
    12. End Class

    CreateSqlCommand aufrufe, ist das dann Sql.cmd oder ist das eine Kopie davon?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Haudruferzappeltnoch schrieb:

    Im Zweifel rutscht der GC nochmal über eine aufgeräumte Sql Instanz, was wahrscheinlich nicht schlimm ist
    Nja, wenn's doof läuft, dann schon, weil doppeldisposen ist nicht erlaubt, das gäbe ne Exception. Klar, durch das Setzen von disposedValue = True sollte das nicht passieren.

    Haudruferzappeltnoch schrieb:

    was ist wenn ich hier CreateSqlCommand aufrufe
    Dann bekommt cmd einen neuen Wert. Und der alte Wert von cmd ist nicht mehr zugänglich, wird nicht disposed, geht verloren und schwirrt als Datenleiche im RAM. Memory leak nennt man sowas. Daher besser:

    VB.NET-Quellcode

    1. Friend Function CreateSqlCommand() as SqlCommand
    2. If cmd IsNot Nothing Then cmd.Dispose
    3. cmd = New SqlCommand(SqlString, con)
    4. Return cmd
    5. End Function

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    doppeldisposen ist nicht erlaubt
    Das ist nun was ich für unabdingbaren Bestandteil des Dispose-Patterns halte: Natürlich muss mans so machen, dass ein Mehrfach-Dispose-Aufruf vollkommen unbedenklich ist.
    Also das halte nicht nur ich so, sondern das habich auch mehrfach iwo gelesen, mindestens auch bei MS.
    Aber mit der disposed-Variable ist dem ja genüge getan.
    Ich würds allerdings ohne machen - weil je weniger Variablen desto besser:

    VB.NET-Quellcode

    1. Friend Class Sql : Implements IDisposable
    2. Friend Readonly cmd as SqlCommand
    3. ...
    4. Friend Sub New()
    5. dim con = New SqlConnection(ConnString)
    6. cmd = New SqlCommand(SqlString, con)
    7. End Sub
    8. Public Sub Dispose() Implements IDisposable.Dispose
    9. If cmd Is Nothing Then return
    10. cmd.Connection.Dispose
    11. cmd.Dispose
    12. cmd = Nothing
    13. End Sub
    14. End Class