Warum »Form1.Show« und Co. einem irgendwann ins Bein schießen

    • VB.NET

    Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

      Warum »Form1.Show« und Co. einem irgendwann ins Bein schießen

      Es ist ein uraltes Thema, aber es will einfach noch nicht zu allen durchdringen: Wer in seinen VB.Net Codes sowas schreibt

      VB.NET-Quellcode

      1. Form1.Show
      ohne dabei vorher Form1 explizit deklariert und initialisiert zu haben (siehe dazu den ein paar Zeilen weiter unten im verlinkten Thread von RodFromGermany, wie es richtig geht), wird sich irgendwann solche Fragen stellen: »Ich hab doch auf meinem Formular einen Button verändert. Warum sehe ich denn dann keine Veränderungen?« Oder »Ich greife in meinem Code explizit auf mein einziges Formular zu. Aber irgendwie passiert da gar nix. Noch nicht mal eine Fehlermeldung über unerlaubte threadübergreifende Vorgänge.«

      Wie RodFromGermany in seinem Thread zum korrekten Erstellen einer Formularinstanz bereits schrieb:

      RodFromGermany schrieb:

      Dies wird aufgerufen, weil es irgendwo steht (ich frage mich nur wo), weil es die jungen Kollegen (leider) falsch gezeigt bekommen oder weil es einfach, einfach VB6, ist.

      Vielleicht liegt es an Gedanken wie: »Ich hab das immer schon so gemacht.«, »Das hab ich so bei youtube gesehen.« oder »Was habt Ihr denn? Läuft doch bisher.« (Ich erhebe keinen Anspruch auf Vollständigkeit ;) )

      Die Profis wissen es, aber hier nochmal klar ausgedrückt: Wer wie oben ohne Deklaration und korrekte Initialisierung solche Codezeilen wie Form1.Show verwendet, der greift mit dem Teil Form1 nicht auf die Klasse selbst zu. Form1 mag zwar im eigenen Projekt eine Klasse sein (vorausgesetzt, man hat sich noch nicht die 5 Sekunden Zeit genommen, um den Namen sinnvoll zu ändern und somit aussagekräftig zu machen, aber das ist ein anderes Thema), aber
      1. ist es Nonsens, da Form1 in erster Linie mal ein Bauplan ist. Man versuche sich vorzustellen, dass man eine Klasse Auto hat, die eine Methode Fahren() hat. Auto.Fahren() wär dann so, als ob man sagt: »Bauplan des Autos: Fahre los.«
      2. ist in jener Zeile Form1 eine Property der Klasse Form1. Klingt verwirrend, ist aber so, was uns IntelliSense auch beweist:


      Man greift also auf eine mehr oder weniger ominöse Klasseneingeschaft zu. Warum ominös? Weil erst mit folgendem Wissen klar wird, was man sich damit antut:

      MSDN schrieb:


      My.Forms ist ein Beispiel für eine dynamisch erstellte Klasse. Diese Klasse wird für Sie bereitgestellt und definiert eine Factorymethode für jedes Formular in Ihrem Projekt. Wenn Sie über My.Forms.Form1 auf Form zugreifen, prüft die Factorymethode, ob bereits eine Instanz von Form1 geöffnet ist. Wenn dies der Fall ist, wird diese Instanz zurückgegeben. Andernfalls wird eine Instanz von Form1 erstellt und zurückgegeben.
      […]
      Instanzen von My-Objekten werden pro Thread bereitgestellt. […] Die Instanz von My.Computer, die auf Thread 1 zurückgegeben wird, unterscheidet sich von der Instanz von My.Computer, die auf Thread 2 zurückgegeben wird.

      Anders ausgedrückt: Wer mit Threads arbeitet, hat in Thread 1 mit Form1 etwas anderes vor sich als in Thread 2. Daher der gern von mir in diesem Zusammenhang verwendete Begriff »thread-dependent property«.

      Kleiner Beweis:


      Daher ergeben sich weder sichtbare Veränderungen noch Fehlermeldungen wegen unerlaubter threadübergreifender Vorgänge, wie anfangs genannt. Warum auch? Es ist ja alles so, wie Microsoft es wollte. Nur sollte man sich auch darüber im Klaren sein, wenn man entsprechenden Code schreibt. Oder verzapft.
      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.
      Kann man eigentlich in einer normalen WinForms-VB-App verhindern, dass man auf diese ominöse Property überhaupt zugreifen kann? Bei neuen Projekten natürlich, indem man es vermeidet. Aber wie spürt man Stinkhaufen in alten Projekten auf?
      Das ist schnell gemacht. Man schreibt in seine Form-Datei z.B.

      VB.NET-Quellcode

      1. Private Sub New()
      2. InitializeComponent()
      3. End Sub

      Und schwupps, hagelt es Kritik/Fehler, wo Form1 und Co sich versteckt haben. Nun kann man erstmal alle Stellen inspizieren und nachbessern. Aber zum einen läuft der Code dann auch bei korrekten Stellen wie diesen nicht

      VB.NET-Quellcode

      1. Using Dialog As New Form1
      2. Dialog.ShowDialog(Me)
      3. End Using

      Zum anderen hat man nun ein Startproblem:


      Die etwas versteckte und automatisch angelegte Einstiegsprozedur rebelliert. Wie kann man nun weiterprogrammieren und trotzdem verhindern, dass man auf diese Property zugreifen kann?
      Da hilft ein Trick aus dem Singleton-Pattern weiter. Wir schreiben eine Funktion, die uns eine Form-Instanz wiedergibt:

      VB.NET-Quellcode

      1. Public Class Form1
      2. Private Shared Instance As Form1 = Nothing
      3. Public Shared Function CreateInstance() As Form1
      4. If Instance IsNot Nothing AndAlso Not Instance.IsDisposed Then Return Instance
      5. Dim SyncLocker As New Object
      6. SyncLock SyncLocker
      7. Instance = New FrmMain
      8. End SyncLock
      9. Return Instance
      10. End Function
      11. Private Sub New()
      12. InitializeComponent()
      13. End Sub


      Dann kann in die Startprozedur:

      VB.NET-Quellcode

      1. Me.MainForm = Global.WindowsApp2.Form1.CreateInstance


      Und was macht man mit all den guten

      VB.NET-Quellcode

      1. Using Dialog As New Form1
      2. Dialog.ShowDialog(Me)
      3. End Using


      Die Lösung ist naheliegend:

      VB.NET-Quellcode

      1. Using Dialog = Form1.CreateInstance
      2. Dialog.ShowDialog(Me)
      3. End Using


      Das dürfte den grundlegenden Fällen von Form1.Show vorbeugen und die meisten zwangsläufigen Folgen beseitigen.
      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.

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

      eine kleine Küchenanalogie zur Vorweihnachtszeit

      Wir nehmen etwas Teig, ein paar Metallförmchen und stanzen ein paar Figuren.
      Was das mit Programmieren zu tun hat?
      Dann den Satz etwas anders: Wir nehmen etwas freien Arbeitsspeicher, ein paar Klassen und instanziieren ein paar Objekte.

      Ja, den Datenbrei des freien Arbeitsspeichers könnte man bei dieser Küchenanalogie als Teig bezeichnen.

      roher Teig als RAM-Analogie

      So viele Sachen, die man damit anstellen kann. So viele Figuren, die man ausstechen kann. Aber wenn man zuviele Figuren ausgestochen hat, ist kein Teig mehr da, um weitere Figuren auszustechen.

      es hat sich ausgestanzt

      Oder in der Programmierküche: Wenn man zuviele Klassen instanziiert hat, ist der freie RAM irgendwann nicht mehr ausreichend.

      Instanz? Stanzen? Hm, klingt ähnlich.

      Klassen sind unsere Metallformen, mit denen wir aus dem freien Arbeitsspeicher Objekte ausstechen/stanzen. Oder eben instanziieren. Wie? Mithilfe von New:

      VB.NET-Quellcode

      1. Public Class MeineKlasse
      2. Property Text As String
      3. End Class
      4. Public Class MeineMetallform
      5. Property Name As String
      6. Property Umrisspunkte As List(Of Drawing.Point)
      7. End Class
      8. '…
      9. Dim MeinObjekt As New MeineKlasse
      10. Dim MeineFigur As New MeineMetallform


      Wenn wir 20 Figuren aus dem Teig ausstechen, haben wir erstmal 20 gleiche Figuren. Mit jeder Figur kann man was anderes machen. Eine wird mit Lebensmittelfarbe grün angemalt, die nächste bekommt Zuckerperlen, die nächste wird gegessen. Egal, was man mit einer Figur macht, es beeinflusst die restlichen nicht.

      Teigforminstanzen, die unterschiedlich bearbeitet wurden; oben links die Metallformvorlage

      Genauso ist es bei Klassen und den Objekten, die wir erstellen/instanziieren. Die Klasse ist die Metallform. Erstellen wir 20 Objekte mithilfe dieser Klasse, haben wir erstmal 20 gleiche Objekte, die aber alle für sich geändert werden können, ohne dass die anderen beeinflusst werden.

      VB.NET-Quellcode

      1. Dim StandardButton As New Button
      2. Dim RoterButton As New Button
      3. Dim GrünerButton As New Button
      4. 'erstmal sind alle gleich, auch wenn der Name anders ist und was anderes suggeriert. Die Änderung kommt jetzt:
      5. RoterButton.BackColor = Drawing.Color.Red
      6. GrünerButton.BackColor = Drawing.Color.Green


      Ändern wir nun die Metallform, entstehen andere Figuren aus dem Teig. Ändern wir die Klasse, entstehen andere Objekte.

      eine Metallformänderung erzeugt nun andere Figürchen

      Nun sollte erstmal klar sein, was es mit Klassen und Instanzen auf sich hat.

      Nun zum eigentlichen Thema Besonderheit von WinForms-Form: Form steht nicht für Metallform, sondern Formular (englisch Form). Das eigentlich Hinterlistige an der Geschichte ist, dass Forms keine normalen Klassen sind, wie in Post#1 bewiesen. Wenn man sagt: Form1.BackgroundColor = Drawing.Color.Red, meckert der Compiler nicht, sondern es ändert sich zur Laufzeit die Hintergrundfarbe des existierenden Formulars Form1.
      Aber Moment!
      Form1.BackgroundColor = Drawing.Color.Red kann nach der oben genannten Logik bzw. Backteiganalogie doch gar nicht gehen. Man kann doch nicht die Farbe der Ausstanzform ändern. Und selbst wenn, dann kann das doch gar keinen Einfluss auf die damit ausgestanzten Teigfiguren haben!
      Korrekt!
      Denn der Code macht was anderes als man glaubt. Er ändert gar nix an der Ausstanzmetallformvorlage. Die wird durch den Code noch nicht mal angesprochen. Ach, und was wird dann angesprochen? Eine ausgestanzte Figur, die sich nur Form1 nennt! Und deren Hintergrundfarbe wird geändert.

      das Fiese an der Sache

      Das ist das Gemeine an der Geschichte, die manche nicht wissen. Man glaubt, dass man mit Form1.BackgroundColor die Klasse an sich anspricht, aber in Wirklichkeit spricht man ein heimlich erstelltes/instanziiertes Objekt an, welches sich so nennt wie die Klasse selbst. Wenn man das begriffen hat, wird so mancher Fehler in der Programmierung und so manche Meckerei im Forum vielleicht auch einleuchtend.

      Eine schöne Adventszeit ^^
      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.

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