Stimmt es, dass eine private Bitmap als ReadOnly angesehen wird?

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

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von Bartosz.

    Stimmt es, dass eine private Bitmap als ReadOnly angesehen wird?

    Hi, kurze Frage am Abend.
    Ich habe eine Bitmap global deklariert, um sie überall verwenden zu können. So wird verhindert, dass mehrere Instanzen gebildet werden müssen. Der FxCopAnalyzer sagt mal wieder "Das Bitmap wird nie disposed.", obwohl ich das tue. Dann habe ich diesen Post gefunden:

    stackoverflow.com/questions/34…idisposable-backing-field
    Hier wird geschrieben, dass
    1) Der FxCopAnalyzer einen Bug hat und
    2)
    The reason is that ..., because it did not have a set accessor, was read-only. Stimmt das?

    Ich verwende nun dies und ich erhalte keine Fehlermeldung mehr:

    VB.NET-Quellcode

    1. Public Property Bild_zum_mit_arbeiten1 As Bitmap
    2. Get
    3. Return Bild_zum_mit_arbeiten
    4. End Get
    5. Set(value As Bitmap)
    6. Bild_zum_mit_arbeiten = value
    7. End Set
    8. End Property
    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.
    Jo, diese dolle Tools blicken eben manchmal einfach nicht richtig durch. Ist natürlich Quatsch, was FxCopAnalyzer dir da erzählt.
    Und selbst wenn es so wäre - dass die Bitmap nie disposed würde - Bei einer readonly globalen Instanz wäre das doch völlig ok, und kein Grund für FxCopAnalyzer, da rumzumeiern.
    Ah - deine Property ist nicht global.
    Ja - schon mussich FxCopAnalyzer irgendwie rechtgeben: Wenn die Klasse verfällt, wo diese Property drinne steht, dann hängt die Bitmap undisposed im Speicher rum.

    eine globale readonly Instanz geht so:

    VB.NET-Quellcode

    1. public shared readonly Bild_zum_mit_arbeiten1 As Bitmap = my.Resources.Bild_zum_mit_arbeiten1


    Aber ich verstehe auch nicht, warum er's Maul hält, wenn du der Property einen (unangebrachten) Setter verpasst.

    (Also ich lass mir von FxCopAnalyzer nicht reinreden. Ich weiss, was ich tu, und ich weisses besser als dieser Automat)
    diese dolle Tools blicken eben manchmal einfach nicht richtig durch.
    Danke dir!!
    Nur einmal zur Info: Vorher wars global private

    VB.NET-Quellcode

    1. Private Bild_zum_Mit_arbeiten as Bitmap
    ,später Pfad mit New einen Pfad zugewiesen und da hat er gemeckert.
    *nacheditiert, da vorher fehlerhaft aus dem Kopf aufgeschrieben.
    Nun ist es

    VB.NET-Quellcode

    1. Private Bild_zum_mit_arbeiten As Bitmap
    2. Public Property Bild_zum_mit_arbeiten1 As Bitmap
    3. Get
    4. Return Bild_zum_mit_arbeiten
    5. End Get
    6. Set(value As Bitmap)
    7. Bild_zum_mit_arbeiten = value
    8. End Set
    9. End Property

    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Bartosz“ ()

    Private ist fast immer innerhalb einer Klasse, also instanzgebunden. Und außerdem nicht sichtbar. Von daher: Was soll daran global sein?
    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.

    ErfinderDesRades schrieb:

    Ja - schon mussich FxCopAnalyzer irgendwie rechtgeben: Wenn die Klasse verfällt, wo die Bitmap drinne steht, dann hängt die Bitmap undisposed im Speicher rum.
    Das gilt ebenso für eine private Klassen-Variable.
    Hatter recht: Das musst du disposen, wenn die Klasse verfällt.
    Oder du machst das Feld Shared Readonly, dann bleibt es im Anwendungs-Scope, wenn die Klasse verfällt.
    Du könntest auch fragen, was ich damit meine, wenn ich sage "wenn die Klasse verfällt".
    Du könntest auch sagen, was das für eine Klasse ist.
    @ErfinderDesRades Ja, aber ich hatte im Form_Closing .dispose() geschrieben, dennoch hatte er gemeckert. Mit diesem (unangebrachten) Setter nicht mehr. Ist wirklich merkwürdig...

    Edit:
    Du könntest auch fragen, was ich damit meine, wenn ich sage "wenn die Klasse verfällt".
    Naja, beim Programm-Aus?

    Du könntest auch sagen, was das für eine Klasse ist.
    Public Class Form1

    Bartosz schrieb:

    Du könntest auch fragen, was ich damit meine, wenn ich sage "wenn die Klasse verfällt".

    Naja, beim Programm-Aus?
    Nee, Ein Form zB verfällt, wenn es geschlossen wird. Das heisst ja nicht, dass das Programm dann aus ist. Das ist eben das Risiko solcher Geschichten, da macht der User das Form vlt. 10 mal auf und zu, und dann hängen 10 solche Bitmapse im Speicher rum.



    Bartosz schrieb:

    Ja, aber ich hatte im Form_Closing .dispose() geschrieben, dennoch hatte er gemeckert.
    Dassis vonne Idee her genau richtig, und eigentlich gibts da nix zu meckern.
    Vermutlich hastes iwie falsch gemacht - etwa wenn du wirklich nur .dispose() hinschreibst, würd ich auch meckern als Compiler.
    also was hast du wirklcih hingeschrieben, wo "er" (wer auch immer das sein mag: Compiler? FxCop? der grosse Bruder?) gemeckert hat.
    Bei mir kommt (bei meiner Implementierung, keine Ahnung, wie die originale aussieht) der Hinweis: Man solle doch bitte die Bitmap in der Dispose-Prozedur von Form1 disposen. Wahrscheinlich kann FxCop einfach nicht riechen, dass der FormClosing-EventHandler so ziemlich immer am Ende aufgerufen wird. Ist für ihn eben nur einer von vielen EventHandlern.
    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.
    Man solle doch bitte die Bitmap in der Dispose-Prozedur von Form1 disposen.
    Ja richtig. Dann war ich in der Form1.designer.vb und habe dort versucht zu disposen, die Warnung blieb aber.

    VB.NET-Quellcode

    1. Protected Overrides Sub Dispose(ByVal disposing As Boolean)
    2. Try
    3. If disposing AndAlso components IsNot Nothing Then
    4. components.Dispose()
    5. End If
    6. Finally
    7. MyBase.Dispose(disposing)
    8. End Try
    9. End Sub


    wo "er" (wer auch immer das sein mag: Compiler? FxCop? der grosse Bruder?)
    FxCop

    Vermutlich hastes iwie falsch gemacht

    So, Folgendes:
    Ich hatte geschrieben:
    Private Bild_zum_mit_arbeiten As Bitmap()
    Mit einem OpenFileDialog im Button1_Click wurde ein neuer Pfad zugewiesen.
    Bild_zum_mit_arbeiten = New Bitmap(Pfad_Bild)

    In der Form_Closing, und in einem KeyEvent, wurde testhalber sowohl das Bild der PictureBox als auch das bild_zum_mit_arbeiten disposed (dabei wurde eigentlich der PictureBox dasselbe Bitmap zugewiesen - keine neue Instanz).

    VB.NET-Quellcode

    1. Private Sub Form_Main_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    2. '...
    3. ElseIf e.KeyCode = Keys.Delete AndAlso PictureBox1.Image IsNot Nothing Then
    4. PictureBox1.Image.Dispose()
    5. Bild_zum_mit_arbeiten.Dispose()

    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.

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

    Meine Meinung: .Designer-Dateien sind für den Designer - da sollte man keinen userCode anbringen.
    Was ich gerne mache, ich verlager die Dispose-Methode aus dem Designer in den UserCode.
    Wie dem auch sei: ich sehe da kein Disposing einer Bitmap. :|



    Bartosz schrieb:

    Mit einem OpenFileDialog im Button1_Click wurde ein neuer Pfad zugewiesen.
    Dann wirds bisserl anspruchsvoller, und da ist ja auch nix mehr mit Readonly, wenn du die Bilderle auswechseln willst.
    Jo, und ist klar: Das alte Bild disposen bevor du das neue reintust.
    Und wie (vom Cop) gesagt: Wenn das Form disposed, dann muss auch die Bitmap disposed werden.

    Übrigens: Wenn das Bild nur für die Picbox da ist, täte ich empfehlen, die Variable zu löschen.

    Noch übriger: Viel besser ist, Picbox.ImageLocation zu verwenden statt Picbox.Image. Dann kümmert sich die Picbox ums laden und disposen.

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

    @Bartosz Wenn im Getter und im Setter nix passiert, kannst Du ohne Hilfsvariable einfach

    VB.NET-Quellcode

    1. Public Shared Property Bild_zum_mit_arbeiten As Bitmap
    schreiben.
    ===
    Und:

    VB.NET-Quellcode

    1. If PictureBox1.Image IsNot Nothing
    2. PictureBox1.Image.Dispose()
    3. End If

    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!
    Was denkst du, warum wollte FxCop dies nicht akzeptieren? Wie geht das besser?

    VB.NET-Quellcode

    1. PictureBox1.Image.Dispose()
    2. Bild_zum_mit_arbeiten.Dispose()


    Ein weiteres Problem entdeckt:
    Ich habe gerade festgestellt, dass er in die Form1.Designer.vb gar nicht reinläuft. Habe ich mit Haltepunkt getestet. (0 Verweise, siehe Anhang).


    Da dann wirds bisserl anspruchsvoller, und da ist ja auch nix mehr mit Readonly,
    nee, es ging darum, dass jemand bei Stackoverflow (siehe Link aus Post 1) geschrieben hat, dass, wenn man keinen Setter verwendet, die Variable ReadOnly wäre.

    Edit 2:
    Übrigens: Wenn das Bild nur für die Picbox da ist, täte ich empfehlen, die Variable zu löschen.
    Wie geht das?

    Bilder
    • Screenshot 2021-01-10 191925.png

      63,63 kB, 986×473, 46 mal angesehen

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

    Bartosz schrieb:

    Wie geht das besser?
    Da müsstest Du schreiben, wie beide Instanzen miteinander verwoben sind.
    Mein Nothing-Test stellt sicher, dass kein Null-Pointer-Zugriff passiert, wenn kein Bild zugewiesen wurde.
    Wenn beide Instanzen identisch sind, musst Du nur eine disposen, allerdings passiert nix, wenn Du versuchst, eine Instanz zwei Mal zu disposen, dies hier knallt nicht:

    VB.NET-Quellcode

    1. Bild_zum_mit_arbeiten = New Bitmap(10, 10)
    2. Bild_zum_mit_arbeiten.Dispose()
    3. Bild_zum_mit_arbeiten.Dispose()
    4. Bild_zum_mit_arbeiten.Dispose()
    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!

    Bartosz schrieb:

    Edit: Ich habe gerade festgestellt, dass er in die Form1.Designer.vb gar nicht reinläuft. Habe ich mit Haltepunkt getestet. (0 Verweise, siehe Anhang).
    Das liegt vermutlich am DebuggerNonUserCode-Attribut.
    Machs weg, das DebuggerNonUserCode-Attribut - wäre ja auch gelogen, weil du willst da doch UserCode einfügen.



    Bartosz schrieb:

    Was denkst du, warum wollte FxCop dies nicht akzeptieren? Wie geht das besser?
    Keine Ahnung.
    Kommt vermutlich drauf an, wo die Zeilen stehen.
    Ich bin ja immer dafür, ganze Methoden zu posten - meinet wegen mit Auslassungen, aber so ein Methoden-Header ist doch was feines.
    Dann kann man vielleicht die Frage beantworten, vielleicht.



    Bartosz schrieb:

    nee, es ging darum, dass jemand bei Stackoverflow (siehe Link aus Post 1) geschrieben hat, dass, wenn man keinen Setter verwendet, die Variable ReadOnly wäre.
    Jo - ich red auch viel, wenn der Tag lang ist.
    (jetzt habich glatt mal nachgeguckt - da steht eiglich nicht was du sagst, was da stehe.)



    (Jetzt kommt was ganz schwieriges:)

    Bartosz schrieb:

    Übrigens: Wenn das Bild nur für die Picbox da ist, täte ich empfehlen, die Variable zu löschen.

    Wie geht das?
    Den Cursor in die Leer-Zeile über der Variablen-Deklaration setzen.
    Dann folgende Tasten-Folge drücken: [Shift-ArrowDown] [Shift-ArrowDown] [Delete]
    (Man kann es auch mit der Maus bewerkstelligen - ist aber schriftlich schwieriger zu erklären.)

    Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @RodFromGermany Ich verwende mittlerweile dein vorgeschlagenes

    VB.NET-Quellcode

    1. Public Shared Property Bild_zum_mit_arbeiten1 As Bitmap
    , ohne dass FxCop meckert.
    Da müsstest Du schreiben, wie beide Instanzen miteinander verwoben sind.

    VB.NET-Quellcode

    1. Public Shared Property Bild_zum_mit_arbeiten1 As Bitmap
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. SollGezeichnetWerden = True
    4. Using OFD As New CommonOpenFileDialog
    5. OFD.Title = "Datei zum Öffnen auswählen"
    6. OFD.Filters.Add(New CommonFileDialogFilter("Bilder", ".jpg;.jpeg;.bmp"))
    7. OFD.Filters.Add(New CommonFileDialogFilter("PNG", ".png"))
    8. OFD.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
    9. If OFD.ShowDialog() = CommonFileDialogResult.Ok Then
    10. Pfad_Bild = OFD.FileName
    11. Pfad_speichern = Pfad_Bild.Substring(0, Pfad_Bild.LastIndexOf("\", StringComparison.Ordinal) + 1) ' for example "C:\Users\myName\Pictures\"
    12. Else
    13. Return
    14. End If
    15. End Using
    16. Bild_zum_mit_arbeiten1 = New Bitmap(Pfad_Bild)
    17. PictureBox1.Image = Bild_zum_mit_arbeiten1

    Und dann wird an Bild_zum_mit_arbeiten1 mittels .GetPixel(x,y) gewerkelt. Später wird wie beschrieben

    VB.NET-Quellcode

    1. Private Sub Form_Main_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    2. '=============================================
    3. ' PictureBox leeren
    4. '=============================================
    5. If e.KeyCode = Keys.Back AndAlso PictureBox1.Image IsNot Nothing Then
    6. '
    7. ElseIf e.KeyCode = Keys.Delete AndAlso PictureBox1.Image IsNot Nothing Then
    8. PictureBox1.Image.Dispose()
    9. Bild_zum_mit_arbeiten1.Dispose()
    10. SollGezeichnetWerden = False
    11. End If
    verwendet.
    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.

    RodFromGermany schrieb:

    Mein Nothing-Test stellt sicher, dass kein Null-Pointer-Zugriff passiert, wenn kein Bild zugewiesen wurde.

    Dafür hatte ich damals meine DisposeAndNullify-Extension erstellt.

    VB.NET-Quellcode

    1. <Extension> Public Sub DisposeAndNullify(Of T As IDisposable)(ByRef ObjectToDispose As T)
    2. ObjectToDispose?.Dispose()
    3. ObjectToDispose = Nothing
    4. End Sub

    Dann ist Bild_zum_mit_arbeiten nicht nur Disposed, sondern auch gleich Nothing.

    ##########

    @Bartosz: Damit hast Du aber nur eine bedingte Dispos-ierung. Also ist nun gar nicht mehr sichergestellt, dass die Bitmap am Ende disposed 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.
    Dann folgende Tasten-Folge drücken: [Shift-ArrowDown] [Shift-ArrowDown] [Delete]
    Ich dachte, du meinst etwas anderes.
    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.
    Ok, Ich danke euch für die Hilfe. Ich überlege gerade, was wir nun machen. Eigentlich ist alles geklärt.

    Ich würde es jemandem so erklären:
    Ich habe mir eine Private Bitmap deklariert, die im Button1-EventHandler mittels eines OpenFileDialogs neu zugeordnet wird. Diese Instanz wird für alles verwendet (PictureBox und das Arbeiten am Bild). Im FormClosing-Eventhandler wird das Bitmap disposed.
    Das Analysetool FxCopAnalyzer hatte sich beschwert, man solle doch bitte die Bitmap in der Dispose-Prozedur von Form1 disposen. Daraufhin fand ich einen alten Thread bei stackoverflow. Dort wurde gesagt, dass der FxCopAnalyzer manchmal verbuggt ist bzw. nicht durchsieht bzw. nicht bemerkt, dass der FormClosing-Eventhandler wirklich aufgerufen werden wird. Im Thread bei stackoverflow wurde erwähnt, dass die Bitmap als „ReadOnly“ angesehen wird. Dies fragte ich in einem deutschen Forum nach. Hier wurde mir bestätigt, was oben genannt wurde.
    Auch, wenn der FxCop nicht riecht, dass der FormClosing-EventHandler aufgerufen werden wird, und manchmal nicht durchsieht, blieb die Frage, warum die Änderung von „Private Bild_zum_mit_arbeiten As Bitmap“ zu „Public Shared Property Bild_zum_mit_arbeiten1 As Bitmap“ Besserung brachte. Die Antwort ist, dass die Variable nun im Anwendungs-Scope verbleibt, falls die Klasse verfällt. Wenn nun im FormClosing- oder KeyDown-EventHandler „.dispose“ aufgerufen wird, kann man sich sicher sein, dass immer die richtige Instanz dieser Bitmap getroffen wird.


    Würdet ihr denn diesen Code befürworten?

    VB.NET-Quellcode

    1. #Disable Warning CA1707 ' Bezeichner dürfen keine Unterstriche enthalten
    2. Public NotInheritable Class Form1
    3. Public Shared Property Bild_zum_mit_arbeiten1 As Bitmap
    4. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    5. 'OpenFileDialog hier
    6. Bild_zum_mit_arbeiten1 = New Bitmap("Pfad")
    7. PictureBox1.Image = Bild_zum_mit_arbeiten1
    8. 'hier mit Bild_zum_mit_arbeiten1 weiter arbeiten
    9. End Sub
    10. Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
    11. If PictureBox1.Image IsNot Nothing Then
    12. PictureBox1.Image.Dispose()
    13. End If
    14. End Sub
    15. Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    16. '=============================================
    17. ' PictureBox leeren
    18. '=============================================
    19. If e.KeyCode = Keys.Back AndAlso PictureBox1.Image IsNot Nothing Then
    20. '...
    21. ElseIf e.KeyCode = Keys.Delete AndAlso PictureBox1.Image IsNot Nothing Then
    22. 'PictureBox1.Image.Dispose() 'braucht ja nicht, da selbe Instanz
    23. Bild_zum_mit_arbeiten1.Dispose()
    24. End If
    25. End Sub
    26. End Class
    27. #Enable Warning CA1707 ' Bezeichner dürfen keine Unterstriche enthalten

    Naja, an der Benennung kann man noch kräftig arbeiten. Form1, PictureBox1, Bild_zum_mit_arbeiten1 …
    Und auch wenn Form1_KeyDown nicht vollständig inhaltlich zu sehen ist, schlage ich eine Variation der If-Umkehrung vor:

    VB.NET-Quellcode

    1. Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    2. If PictureBox1.Image Is Nothing Then Return
    3. '=============================================
    4. ' PictureBox leeren
    5. '=============================================
    6. If e.KeyCode = Keys.Back Then
    7. '...
    8. ElseIf e.KeyCode = Keys.Delete AndAlso PictureBox1.Image IsNot Nothing Then
    9. 'PictureBox1.Image.Dispose() 'braucht ja nicht, da selbe Instanz
    10. Bild_zum_mit_arbeiten1.Dispose()
    11. End If
    12. End Sub

    Und da nicht klar ist, wieviele Keys ggf. noch (zukünftig) abgefragt werden, ggf. ein Select Case draus machen.
    Und da nicht klar ist, wie komplex der Code in den einzelnen Abzweigungen ist, würde ich diese in eigene Subs auslagern, wenn sie mehr als eine Zeile lang sind.
    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.