redundanzfreie Überprüfung im Programmcode gesucht

  • VB.NET
  • .NET 4.5

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

    redundanzfreie Überprüfung im Programmcode gesucht

    Hallo zusammen.

    Wieder mal eine Grundlagenfrage. Ich habe eine WinForms-Anwendung, bei der ich mit einem Objekt arbeite, welches ich aus einer geladenen Datei erstelle. Dieses Objekt kann ich mithilfe des/der GUI manipulieren. Ich kann die Bearbeitung auch abschließen, sodass kein Objekt mehr zur Manipulation da ist. Um nach Entladen keine Fehler zu erhalten, wenn ich meine CEs auf dem/der GUI betätige, habe ich z.B.:

    VB.NET-Quellcode

    1. Private Sub BtnMoveUp_Click(sender As Object, e As EventArgs) Handles BtnMoveUp.Click
    2. If MyFileObject Is Nothing Then Return
    3. DoThis()
    4. DoThat()
    5. MyFileObject.DoAnotherThing()
    6. End Sub
    Nun habe ich auf dem/der GUI mehrere Möglichkeiten, das Objekt zu manipulieren, also auch viele EventHandler (EH), die irgendwas mit dem Objekt machen. Und in jedem EH steht als erste Zeile die Überprüfungszeile auf Nothing. Gibt es eine Möglichkeit, das so zu ändern, dass sich diese Low-Level-Codezeile nicht dauernd wiederholt (Stichwort DRY, IOSP)? Das Problem ist das Return, das lässt sich schlecht in ne Sub auslagern.
    Folgende Sachen scheiden aus:
    • If-Umkehrung (ich will's nur gesagt haben)
    • CE-Property Enabled auf False setzen und nur nach erfolgreichem Laden der Datei reaktivieren. Auch wenn der Standardnutzer sowas nicht macht: Ich habe mir ein CE-Manipulationsprogramm gebastelt, was den Visible-/Enabled-Status ändern kann. Man kann also ohne extremen Aufwand ein Programm von außen manipulieren. Von daher sehe ich es nicht als akzeptabel, mithilfe von Enabled und Visible eine Programmsteuerung zu erzeugen, also darauf zu hoffen, dass die Buttons etc. nur dann aktiv sind, wenn alles sonst vorher richtig gelaufen ist.
    Ich bin gespannt.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Das einzige was mir hier einfällt wären NULL-Bedingte Operatoren. Also ?() und ?. Weiss jetzt aber nicht ab welchem Framework das zur verfügung steht.

    docs.microsoft.com/de-de/dotne…ull-conditional-operators

    Die Sub ShowMessageBox wird nur gecallt, wenn obj nicht Nothing ist.

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private obj As MyObj
    3. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    4. obj?.ShowMessageBox()
    5. End Sub
    6. End Class
    7. Public Class MyObj
    8. Public Sub ShowMessageBox()
    9. MessageBox.Show("MessageBox")
    10. End Sub
    11. End Class

    Cloud Computer? Nein Danke! Das ist nur ein weiterer Schritt zur totalen Überwachung.
    „Wer die Freiheit aufgibt, um Sicherheit zu gewinnen, wird am Ende beides verlieren.“
    Benjamin Franklin
    Erstmal danke. Das Problem ist nur, dass mit der Variante nicht-Objektbezogene Funktionen aufgerufen werden (DoThis, DoThat). Aber die dürfen eben auch nicht laufen, wenn kein Objektinstanz aus der Datei geladen wurde. Oder anders: Die ganze Sub nach dem If-Statement darf nicht ablaufen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Nunja egal wie man es macht, du hast trotzdem sich wiederhohlenden Code, du musst ja jedes Object jedesmal testen. Egal ob du nun eine Function hättest die ein "Exit Sub" zurückgeben könnte(gibst ja nicht), oder ob du nun schreibst if ... then. Hast du immer diese Zeile zum testen.
    Cloud Computer? Nein Danke! Das ist nur ein weiterer Schritt zur totalen Überwachung.
    „Wer die Freiheit aufgibt, um Sicherheit zu gewinnen, wird am Ende beides verlieren.“
    Benjamin Franklin
    Mir fällt da gerade noch eine ziemlich dubiose Sache ein:
    Alle relevanten Events über einen EH-Kamm scheren und dann ggf. nach sender wieder ausklamüsern <X

    VB.NET-Quellcode

    1. Private Sub EventHandlerForAll(sender As Object, e As EventArgs) Handles BtnUp.Click, BtnDown.Click, TvList.AfterSelect '...
    2. If MyFileObject Is Nothing Then Return
    3. Select Case True
    4. sender is BtnUp: DoThis(): DoThat(): MyFileObject.DoAnotherThing()
    5. sender is BtnDown: '...
    6. sender is TvList: '...
    7. End Select
    8. End Sub
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Nee, sorry. Sollte ne Abkürzung für TreeView sein.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Wenn es dir tatsächlich nur darum geht, DRY einzuhalten und somit das Copy&Paste zu wiederholen, kannst du diese Schiene fahren:

    (Hier C# - VB ist bei mir leider zu lange her).

    C#-Quellcode

    1. private void WithActiveFileObject(Func<YourFileObject> fn)
    2. {
    3. if (myFileObject is null)
    4. {
    5. fn(myFileObject);
    6. }
    7. }
    8. private void Handle_Foo() => WithActiveFileObject(obj =>
    9. {
    10. DoStuff();
    11. });


    Inwiefern das besser ist als der Einzeiler, sei mal dahingestellt.

    VaporiZed schrieb:

    Auch wenn der Standardnutzer sowas nicht macht: Ich habe
    mir ein CE-Manipulationsprogramm gebastelt, was den Visible-/Enabled-Status ändern kann. Man kann also ohne extremen Aufwand ein Programm von außen manipulieren.

    Hierzu möchte ich allerdings anmerken, dass das kein Grund sein sollte, seinen Code zuverkomplizieren. Als Analogie: Wenn ich ein Auto kaufe und den Bordcomputer von außen manipuliere um Sachen zu ermöglichen, die normalerweise nicht gehen sollten, ist es absolut zu erwarten, dass das Auto danach nicht mehr gescheit tut. Das gleiche gilt für jedes Programm. Wenn man von außen manipulieren anfängt, braucht man sich nicht wundern, wenn eine App crashed. Durch das Manipulieren wird ein unerwarteter State im Programm erzeugt (quasi ein "exceptional state" ;) ), der dann eben auch früher oder später zu einer Exception führen wird.
    Eine Funktionsadresse als Parameter. Gute Idee. Würde ich mehr C++/C# programmieren, wären mir irgendwann vielleicht wieder Pointer in den Sinn gekommen.

    VB.NET-Quellcode

    1. Public Class FrmMain
    2. Private TestInstance As TestClass = Nothing
    3. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    4. TestTheObjectBeforeCalling(AddressOf MuchToDo)
    5. TestInstance = New TestClass
    6. End Sub
    7. Private Sub TestTheObjectBeforeCalling(Action As Action)
    8. If TestInstance Is Nothing Then Return
    9. Action()
    10. End Sub
    11. Private Sub MuchToDo()
    12. MessageBox.Show("phew!")
    13. End Sub
    14. End Class
    15. Public Class TestClass
    16. End Class
    1. Klick läuft ins Leere, erzeugt aber eine Instanz, der 2. Klick führt die Sub dann aus.
    Ist zwar wahrscheinlich ein gutes Stück am C#-Original vorbei, aber ich glaub ich hab das Prinzip verstanden.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

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

    VaporiZed schrieb:

    Ist zwar wahrscheinlich ein gutes Stück am C#-Original vorbei


    Ne, das passt schon so ;)
    Die einzige Empfehlung wäre nur, Lambdas anstatt AddressOf zu nutzen (natürlich nur so lange es sinnvoll ist), damit alles an einem Fleck steht und 'MuchToDo' nicht aus dem Kontext gerissen wird. Sonst passt alles.
    Bei dem Code sehe ich aber das Problem, das wenn bereits eine Instanz eines Objectes da ist, Member dieser Objectinstanz verändert wurden, hat man wieder den Instanzierungs-Zustand nach einem Button Click.
    Cloud Computer? Nein Danke! Das ist nur ein weiterer Schritt zur totalen Überwachung.
    „Wer die Freiheit aufgibt, um Sicherheit zu gewinnen, wird am Ende beides verlieren.“
    Benjamin Franklin
    @shad: Ah, klar. Statt

    VB.NET-Quellcode

    1. TestTheObjectBeforeCalling(AddressOf MuchToDo)
    z.B.

    VB.NET-Quellcode

    1. TestTheObjectBeforeCalling(Sub() MuchToDo)
    Hat den Vorteil, dass es kürzer ist und man auch Parameter an die Subs geben kann, also z.B.

    VB.NET-Quellcode

    1. Private Sub RenameItTo(NewName As String)
    2. TestTheObjectBeforeCalling(Sub() MuchToDo2(NewName))
    3. End Sub

    Das geht bei AddressOf natürlich nicht.

    @NoIde: Ich vermute mal, dass Du Post#9, Zeile#6 meinst. Wenn ja: Klar, das ist natürlich für ein echtes Projekt Hunz. Damit wollte ich nur schnell die Instanziierung dazutun, ohne jetzt ne eigene Sub dafür herzunehmen. Falls Du das nicht meintest, bitte sagen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    @VaporiZed Ich würde Functions draus machen.

    VB.NET-Quellcode

    1. Dim flag = True
    2. If flag Then flag = DoThis()
    3. If flag Then flag = DoThat()
    4. Return flag
    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    Hat aber die Nachteile, dass ich Subs in Boolean-Functions umwandeln muss, wieder Ifs drin habe, und die Situation, dass ich nicht für jede einzelne Prozedur entscheiden will, ob's weitergeht, sondern nur einmal, ganz zum Angang. Abhängig davon, ob ein Object existiert oder nicht.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Du kannst natürlich auch mit eigenen Exceptions arbeiten, wenn fail, dann throw ExAny und mit catch kommst Du dann genau da raus, wo es weitergeht bei fail.
    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    Mir dämmert, dass wir das schon mal bei meinem IOSP-Thread hatten.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Abhängig von der Struktur wäre das Null-Object-Pattern eine Möglichkeit derartige Überprüfungen zu vermeiden.

    Edit: Wenn weiterhin "nicht-Objektbezogene Funktionen" aufgerufen werden sollen, müsste der Code allerdings erst entsprechend umgestaltet werden. Von daher vermutlich nicht das was gesucht ist, aber vielleicht hilft das Stichwort ja wen anders :)

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

    VaporiZed schrieb:

    CE-Property Enabled auf False setzen

    Ich stimme da @shad zu. Wenn der User anfängt, so krasse Sachen zu machen, dann kann man sowieso für nichts garantieren. Man könnte sonst auch argumentieren, dass der User im Code

    VB.NET-Quellcode

    1. Sub Foo_Click() Handles Foo.Click
    2. If MyFileObject Then Return
    3. MyFileObject.Bar()
    4. End Sub

    einfach die Prüfung rauseditieren könnte. Entweder dekompilieren und neu kompilieren, oder wie du mit CheatEngine im laufenden Prozess den Code überschreiben.

    Man könnte noch einen Schritt weiter gehen:
    Sagen wir, du hast ein Gerät am Computer angeschlossen, das per serieller Schnittstelle mit deinem Programm kommuniziert. Sendest Du Kommando 1, wechselt das Gerät von Zustand A in Zustand B. Sendest Du das Kommando nochmal, wechselt das Gerät zurück in Zustand A. Der Benutzer kann das Wechseln zu den Zuständen mit jeweils einem Button veranlassen. Natürlich muss der "Zu Zustand B wechseln"-Button deaktiviert sein, wenn sich das Gerät bereits in Zustand B befindet. Dein Programm kann aber nicht mit 100%-iger Sicherheit wissen, in welchem Zustand sich das Gerät befindet. Ich kann dein Programm in einer virtuellen Maschine ausführen, in Zustand A wechseln, dann einen Snapshot erstellen, in Zustand B wechseln und wieder zum Snapshot zurückkehren. Dein Programm denkt, das Gerät wäre in Zustand A, aber es ist in Wirklichkeit in Zustand B. Oder ich klemme die serielle Verbindung vom Computer ab und an einen anderen Computer an und schicke von dort aus das Kommando zum Wechseln.
    Oder, oder, oder.
    Man muss sich die Frage stellen, wie viel Aufwand man betreiben will.

    Man kann sich eigentlich problemlos darauf verlassen, dass die Event-Handler nicht ausgeführt werden, wenn die entsprechenden Controls deaktiviert sind.
    Das muss aber nicht nötigerweise bedeuten, dass man bei jeder Zustandsänderung 50 Zeilen mit ControlXY.Enabled = True haben muss. Deaktiviert man z.B. ein Panel, werden auch alle Controls in dem Panel deaktiviert. Das kann man sich zunutze machen.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils