"Sauberes" Programmieren, Alternativen zu globalen Variablen

  • VB.NET

Es gibt 60 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    "Sauberes" Programmieren, Alternativen zu globalen Variablen

    Hallo,

    ich habe im Netz einiges zur Verwendung von globalen Variablen unter OOP gelesen.
    Meist wird von der Verwendung abgeraten.

    Was ist nun "normalerweise" der Ersatz wenn Werte in mehreren Prozeduren benötigt werden?
    Ist die "richtige" Lösung eine neue Klasse mit den Werten als Eigenschaften?

    Beispiel: Ich benötige 3 Integerwerte an verschiedenen Stellen im Programm, also:

    VB.NET-Quellcode

    1. Public Class globaleWerte
    2. Private _Wert1 as Integer = 0
    3. Private _Wert2 As Integer = -1
    4. Private _Wert3 As Integer = 0
    5. Public Sub New(ByVal Wert1 as Integer, Wert2 as Integer, Wert3 As Integer)
    6. _Wert1 = Wert1
    7. _Wert2 = Wert2
    8. _Wert3 = Wert3
    9. End Sub
    10. Public ReadOnly Property Wert1 As Integer
    11. Get
    12. Return _Wert1
    13. End Get
    14. End Property
    15. End Class
    Sind die Werte immer konstant oder werden die veraendert? Wenn sie konstant sind, koenntest du sie schonmal als const deklarieren. Um sie global zu nutzen legst du entweder eine extra Shared Class an, in der die Werte stehen, dann musst du die Variablen/Konstanten auch als Shared deklarieren, oder du packst sie einfach als Shared Variablen/Konstanten in einer vorhandenen Klasse und greifst dann auf die immer zu. Falls du auf die Idee kommen solltest Module zu nutzen, lass es, denn Module boese, haengt mit VB6 zusammen.

    Gruss
    Jonas Jelonek
    Pauschal kann man das gar nicht beantworten. Es kommt meiner Meinung nach auf den Verwendungszweck der jeweiligen Variablen an, die da "von mehreren Klassen" verwendet werden sollen.

    Meist handelt es sich dabei wohl um irgendwelche Konfigurations-Parameter. In solchen Fällen ist es meine Vorgehensweise, eine eigene Klasse nur für diese Konfigurationsdaten zu erstellen, die sich auch um die Serialisierung und Deserialisierung kümmert, davon dann ein Objekt (oder je nach Anwendung auch mehrere Objekte) davon zu erzeugen und die bei klassenübergreifenden Funktionsaufrufen weiterzureichen.

    Globale Konstanten finden am besten in Enums Platz, Konstanten-"ähnliche" Werte (z.B. Werte, die einmal bei Programmstart ermittelt/berechnet werden, mit PI hat man das z.B. vor .NET ganz gerne gemacht) stelle ich gerne als "Shared"-Eigenschaften/Felder einer statischen (NotInheritable ohne Konstruktor*) Klasse zur Verfügung.

    Module verwende ich mittlerweile nur noch für Erweiterungsmethoden.

    *) Da es ohne Konstruktor-Implementierung immer einen Default-Konstruktor ohne Parameter gibt, erreicht man dies durch Definition eines parameterlosen Konstruktors, der ausschließlich "Private" ist.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Nimm nur Properties, die kannst Du mit dem PropertyGrid gut editieren.

    VB.NET-Quellcode

    1. Public Class globaleWerte
    2. Private _Wert1 As Integer
    3. Public Sub New(ByVal Wert1 As Integer, Wert2 As Integer, Wert3 As Integer)
    4. Me.Wert1 = Wert1
    5. Me.Wert2 = Wert2
    6. Me.Wert3 = Wert3
    7. End Sub
    8. Public Property Wert1 As Integer
    9. Get
    10. Return _Wert1
    11. End Get
    12. Private Set(ByVal value As Integer)
    13. _Wert1 = value
    14. End Set
    15. End Property
    16. Public Property Wert2 As Integer
    17. Public Property Wert3 As Integer
    18. End Class
    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!
    Globale Variablen sind m.M.n. nicht boese.

    VB.NET-Quellcode

    1. Public Shared Class Bla
    2. Public Shared Bla1 As Integer = 0
    3. End Class
    Musst du halt nur noch anpassen.
    Oder wie mein Vorredner schon sagt: Properties, sind besser zu handhaben, die dann aber auch in eine Shared Class
    Public Shared Property BlaBla As Integer
    Ok, dann lag ich falsch, ich habe schon lange nichts mehr mit VB zutun gehabt, arbeite nurnoch mit C#, Java, usw. In C# gibt es schoen public static class, da ist das wesentlich besser. Entweder du erwaegst einen Umstieg zu C#, jedoch solltest du vorher die Grundlagen gut koennen oder du machst es mit einer NotInheritable-Klasse mit privatem Konstruktor.

    Module sind boese, weil sie nicht OOP sind, man benutzt sie nur noch fuer Extensions, fuer andere Sachen immer objektorientierte Loesungen.
    Warum sind Module böse? Sie kommen aus VB6, aber man verwendet sie noch, nämlich für Extensions. Dann ist die Frage, warum man sie sonst nicht nutzen darf/sollte?! Hetzt doch nicht immer dagegen, man darf sie durchaus noch verwenden, auch wenn sie abgelöst wurden.

    Shared und static müsste man auch bestreiten. Statische Sachen sind auch nicht immer sauber, siehe Program.cs, da kannst du auch statische Felder und Properties deklarieren, aber sauber ist es trotzdem nicht.
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Aha und warum sind diese nicht OO ? Wo ist der unterschied zur statischen Klasse ? Wieso pack ich sowas überhaupt in ne Klasse oder n Modul ? Der Sinn dahinter ist die Kapselung. Ich möchte Funktionen und Properties sinnvoll unterordnen. In manchen Sprachen macht mans mit statischen Klassen, in anderen mit Modulen oder einfach mit Namespaces. Wichtig ist da eigentlich nur, dass man nicht alles in eine Klasse/Namespace packt ála Microsoft.VisualBasic. Funktionen hält man natürlich meist nicht statisch oder als Extension.
    Hallo,

    bevor ich Euren Ausführungen gar nicht mehr folgen kann, hier mein aktuellerLösungsansatz:
    (Anmerkung: Mit Schüssen sind Schüsse auf eine Torwand gemeint...;-))

    VB.NET-Quellcode

    1. Public Class SpielDaten
    2. Private _Schuesse As Integer = 0
    3. Private _Schusszahl As Integer = 0
    4. Private _Punktestand As Integer = 0
    5. Private _Rekord As Integer = 0
    6. Public Sub New(ByVal Schuesse As Integer, Schusszahl As Integer, Punktestand As Integer, Rekord As Integer)
    7. _Schuesse = Schuesse
    8. _Schusszahl = Schusszahl
    9. _Punktestand = Punktestand
    10. _Rekord = Rekord
    11. End Sub
    12. Public Shared Property Schuesse As Integer
    13. Get
    14. Return _Schuesse
    15. End Get
    16. Set(value As Integer)
    17. _Schuesse = value
    18. End Set
    19. End Property
    20. Public Property Schusszahl As Integer
    21. Get
    22. Return _Schusszahl
    23. End Get
    24. Set(value As Integer)
    25. _Schusszahl = value
    26. End Set
    27. End Property
    28. .......etc die beiden restlichen Propertys....
    29. End Class


    Ich kann hier aber keine "Public Shared Class" definieren (gibt es nicht).
    Was muss ich jetzt noch ändern?

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

    Ich selber benutze sehr selten solche statischen Klassen, da sie nur teilweise OOP sind, aber immernoch mehr als Module. In Modulen ist immer alles automatisch statisch und auch NotInheritable. Man kann Module nicht mit Interfaces erweitern und sie sind nach aussen hin nicht sichtbar --> keine Wiederverwendbarkeit. Ausserdem muss man nicht gleich statische Klassen nehmen. Man kann auch einfach ne normale Klasse nehmen und darin bestimmte Properties auf static setzen, dann hat man eigentlich auch schon, was man will. Module sind Relikte aus VB6 und VB6 wird nur noch vereinzelt verwendet.

    TE: Wenn du nicht mit statischen Klassen arbeiten willst, kannst du die Werte auch in My.Settings abspeichern, besonders wenn die Werte nach dem Beenden des Programms noch vorhanden sein sollen.

    Jonas Jelonek schrieb:

    In Modulen ist immer alles automatisch statisch und auch NotInheritable.
    ....das könnte daran liegen, dass Module statische Klassen sind (siehe Anhang). Module sind also böse, statische Klassen nicht?

    Es gibt dort 3 leichte Unterschiede zu statischen Klassen, wie man sie aus C# kennt:
    • Die Klassen, die aus Modulen erstellt werden, haben am Ende zusätzlich das StandardModule-Attribut (damit andere VB-IDEs wissen, dass das ursprünglich ein Modul war).
    • Alle Member des Moduls müssen nicht mit dem Modulnamen qualifiziert angegeben werden (verursacht durch das StandardModule-Attribut).
    • Alles, was sich in einer statischen Klasse befindet, darf nur statisch sein. Deshalb hat man sich gedacht, dass man bei den Membern das Shared auch weglassen kann. Hätte manin C# auch machen können, aber okay.

    Trotzdem verhalten sich Module zur Laufzeit exakt so wie statische Klassen aus C#, weil sie nichts anderes sind.

    Sollte man deshalb was anderes benutzen?
    Das ist Geschmackssache. Manche verwenden stattdessen so ein Konstrukt wie das hier:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. NotInheritable Class EineKlasse
    2. Private Sub New()
    3. End Sub
    4. ' Member statisch deklarieren
    5. End Class

    Mein Fall ist das hingegen nicht, da es nicht ganz das selbe Verhalten wie eine statische Klasse hat (Member können immer noch nicht-statisch deklariert werden). Außerdem habe ich bei sowas immer die Befürchtung, dass die CLR diese Klasse anders behandelt als eine "richtige" statische Klasse. Beweisen kann ich Euch das natürlich nicht.

    Ich benutze deshalb Module, wo auch in C# statische Klassen angebracht wären. Dabei qualifiziere ich die Member immer voll (Modulname.Memberfunktion()). Da ich ReSharper benutze, wäre es theoretisch möglich, dieses mit einer Funktion zu erweitern, dass Fehler geworfen werden, wenn man sich nicht daran hält. Das Feature existiert wie gesagt allerdings nocht nicht. Ich habe irgendwann mal einen Feature-Request auf gemacht. Für den hat sich aber offensichtlich noch Niemand (Entwickler und User) interessiert.


    Jonas Jelonek schrieb:

    Man kann Module nicht mit Interfaces erweitern
    Das geht mit statischen klassen in C# übrigens auch nicht. Das liegt eher an Interfaces, die es einem nicht erlauben, statische Member zu definieren. Statische Klassen (oder Module) haben hier nicht die Schuld.

    Jonas Jelonek schrieb:

    sie sind nach aussen hin nicht sichtbar
    Wie? Einfach als Public markieren geht nicht? Das wäre mir neu.
    Bilder
    • modul.png

      6,42 kB, 364×179, 370 mal angesehen
    • klasse.png

      14,71 kB, 712×163, 405 mal angesehen
    Von meinem iPhone gesendet
    Man muss über den Sinn nachdenken.

    Du hast jetzt weit mehr als 30 Zeilen, nur um 4 Variablen zu speichern.
    Eigentlich brauchst du auch keine Properties, denn du verarbeitest mit Get und Set eigentlich garnichts.

    VB.NET-Quellcode

    1. Public Class SpielDaten
    2. Public _Schuesse As Integer
    3. Public _Schusszahl As Integer
    4. Public _Punktestand As Integer
    5. Public _Rekord As Integer
    6. Public Sub New(ByVal Schuesse As Integer, Schusszahl As Integer, Punktestand As Integer, Rekord As Integer)
    7. _Schuesse = Schuesse
    8. _Schusszahl = Schusszahl
    9. _Punktestand = Punktestand
    10. _Rekord = Rekord
    11. End Sub
    12. End Class


    Diese Lösung ist einfach und übersichtlich.
    Und wenn du darin keine Daten verarbeiten willst, sondern nur speichern, dann lohnt sich vllt ein Structure. Würde ungefähr so aussehen:

    VB.NET-Quellcode

    1. Public Structure SpielDaten
    2. Dim Schuesse As Integer
    3. Dim Schusszahl As Integer
    4. Dim Punktestand As Integer
    5. Dim Rekord As Integer
    6. End Structure




    Mfg.
    SAR

    SAR-71 schrieb:

    VB.NET-Quellcode

    1. Public _Schuesse As Integer
    2. Public _Schusszahl As Integer
    3. Public _Punktestand As Integer
    4. Public _Rekord As Integer

    Bei öffentlichen Feldern würde ich das Prefix allerdings eher weglassen - unabhängig davon, was die "Design Richtlinien" von MS dazu sagen. Zuweilen bevorzuge ich in solchen Fällen gar die Deklaration als Properties, was allerdings in C# deutlich eleganter geht:

    C-Quellcode

    1. public int Schuesse { get; set; }
    2. public int Schusszahl { get; set; }
    3. public int Punktestand { get; set; }
    4. public int Rekord { get; set; }
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.

    stepper71 schrieb:

    bevor ich Euren Ausführungen gar nicht mehr folgen kann, hier mein aktuellerLösungsansatz:
    (Anmerkung: Mit Schüssen sind Schüsse auf eine Torwand gemeint...;-))
    Hier könnte malwieder eine Datenverarbeitung vorliegen, und die Rede ist von einem Detail des Datenmodells, nämlich Summenwerte von Datensätzen.
    Die alte Leier: Datenverarbeitung, Datenmodell, Highlander-Prinzip ("es kann nur einen geben"), typisiertes Dataset, Databinding, formübergreifendes Databinding, blablafaselfasel...
    Wovon ich abrate ist, dieses Detail aus dem Zusammenhang des übrigen Datenmodells herauszunehmen.

    Ich häng das normal nicht die ganze Zeit an die große Glocke, aber ein Datenmodell ist auch sowas: Eine globale Variable, von überall zugreifbar. Ich bin da auch nicht so ganz glücklich mit, dasses nicht anders geht, aber es geht nicht anders. Und deshalb seh ich auch zu, dass alle Belange mit ins Datenmodell hinein kommen, damits so Gott will nur eine sone böse globale Variable gibt.

    Meist wird von der Verwendung abgeraten.
    ja, und?
    Ich hoffe, du hast noch weitere Gründe, als nur, dass annere Leuts das schlimm finden.

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

    Hallo,

    ich habe das jetzt (nach einigen Tipps von ErfinderdesRades), da ich in dem Form ein Dataset habe, wie folgt gelöst:

    VB.NET-Quellcode

    1. Partial Class TorwandDataSet
    2. Public intSchuesse As Integer
    3. Public intSchussanzahl As Integer
    4. Public intRekord As Integer
    5. Public intPunktestand As Integer
    6. End Class


    Nochmal vielen Dank für die vielen Infos und Anregungen.