Ohne global in diesem Fall

  • Hallo!

    Wie ich hier auch schon öfters gelesen habe solte man ja global verhindern, Seiteneffekte auf Grund des "zuballerns" des Namensraumes etc.. Nun Hätte ich für ein spezielles Szenario eine Frage wie ich das hinbekomme:

    Ich habe eine GUI, darin kann man u.a. eine Datei öffnen, es werden einige Prüfungen durchgeführt und dann quasi ein ErgebnisLog angezeigt. Nun gehen wir mal davon aus, der Anwender (das will ich so haben!) öffnet die geprüfte Datei parallel im Edior und fixt einen nach den anderem Fehler.

    Nun gibt es in der GUI einen Button "Datei erneut prüfen" dieser prüft nach klick die vorhin bereits geprüfte Datei erneut. Und da liegt mein Hund begraben. Wie bekomme ich ohne globals den Dateinamen vom ersten "Datei öffnen" Vorgang nun zu der Funktion die der Button "Datei erneut prüfen" ausführt? MIr ist der Weg des Pfades nicht klar, den dieser als Parameter über die Funktion geht die die GUI aufbaut mit der (ich nenn die so) "Listener-Endlos-Schleife".

    Ich bekomme es ohne global nicht hin.. Ist jetzt keine Tragik, aber ich wollte es hier gerne mal gesagt haben, ev. fehlt mir ja noch dies oder das zum logischen Aufbau/Workflow von so einem "Tool"

    Danke!

    Einmal editiert, zuletzt von hausl78 (23. April 2014 um 00:19)

  • Moin!

    Da du leider kein Skript zum Spielen mitlieferst, kann man nur erahnen, wie dein Programm aufgebaut ist.
    Ich vermute, du befindest in im "OnEvent-Modus" und hast auf globaler Ebene nur eine While/Wend Endlosschleife. Alle Aufgaben werden in Funktionen und somit auf lokaler Ebene abgearbeitet, wodurch der Wert einer Variablen nach Verlassen der Funktion (also des Gültigkeitsbereichs) verloren geht.
    Um das zu verhindern, gibt es das Schlüsselwort Static.
    Achtung! Die Funktion scheint in der AutoIt Version 3.3.8.1 Bugs zu haben, die aber in der neuesten Version gefixt sind. Siehe "Statische Variablen und AdlibRegister. Bug oder normales Verhalten?"
    Eine hiermit deklarierte Variable behält zwar ihren Wert nach Verlassen des Gültigkeitsbereichs, ist aber dennoch nur innerhalb dessen sichtbar. Somit belegt eine solche Variable zwar Arbeitsspeicher, "ballert" aber nicht den Namensraum zu, wie du es formuliert hast.
    In deinem speziellen Fall würde sich allerdings auch eine globale Variable anbieten, man muß ja nicht völlig drauf verzichten ;)
    Ansonsten könntest du auch überlegen, die Werte in einer Ini zu speichern, was den Vorteil hätte, dass diese auch nach Neustart des Programes verfügbar wären.


    Sanfte Grüße :D

  • Denkbar wäre sowas:

    [autoit]


    Func _StringStore($sData = Default)
    Local Static $sLocalData = Null
    If $sData = Default Then Return $sLocalData
    $sLocalData = $sData
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    ConsoleWrite(_StringStore() & @LF) ;Da noch kein Wert übergeben worden ist, handelt es sich um einen Null-String
    _StringStore("Mein String!") ;Wir übergeben jetzt einen String...
    ConsoleWrite(_StringStore() & @LF) ;... Und siehe da!
    ;Natürlich ist noch alles veränderbar
    _StringStore("Hello, World!")
    ConsoleWrite(_StringStore() & @LF)

    [/autoit]


    Das könnte man mit einem Dictionary innerhalb der _StringStore-Funktionauch noch universeller halten... Ich poste nachher mal ein Beispiel.

    Gruß

    Edit: Sowas meinte ich:

    Spoiler anzeigen
    [autoit]


    ConsoleWrite(var("myvar") & @LF) ;Da noch kein Wert übergeben worden ist, handelt es sich um einen Null-String
    var("myvar", "Mein Text!") ;Wir übergeben jetzt einen String...
    ConsoleWrite(var("myvar") & @LF) ;... Und siehe da!
    ;Natürlich ist noch alles veränderbar
    var("myvar", "Hello, World!")
    ConsoleWrite(var("myvar") & @LF)
    ;Auch Schnellzugriff (letzte verwendete Variable) geht!
    ConsoleWrite(var() & @LF)

    [/autoit] [autoit][/autoit] [autoit]

    Func var($sKey = Default, $sValue = Default)
    Local Static $oData = ObjCreate("Scripting.Dictionary")
    Local Static $sLastKey = Null
    If $sKey = Default Then Return $oData($sLastKey)
    If $sValue = Default Then Return $oData($sKey)
    $sLastKey = $sKey
    $oData($sKey) = $sValue
    EndFunc

    [/autoit]
  • chesstiger
    Was soll das denn für eine Lösung sein?
    Statt direkt globale Variablen hinzuschreiben verpackst du die nur in einen Wrapper.
    Du hast zwar vermieden explizit eine globale Variable zu schreiben hast aber den selben Effekt - mit den selben Problemen.
    Alle Codeteile greifen weiterhin auf die eine Variable über ihren Namen zu (die halt nur in der Funktion als Static Dictionary verpackt sind) und können Sie verändern wie sie wollen egal ob das Auswirkungen auf andere Teile haben könnte.

    hausl78
    Lass es global.
    Besonders bei kleineren Ein-Mann-Skripten ist das Problem nicht gravierend.
    Es bringt dir nichts deinen Code unleserlich und nicht wieder verwendbar zu machen in dem du krampfhaft versuchst eine globale Variable zu vermeiden.

    In AutoIt gibt es prinzipiell nur zwei Definitionsbereiche - außerhalb von Funktionen (global) und innerhalb von Funktionen (lokal).
    Wenn der Dateinamen nicht global sein soll darf er ergo nur für die Dauer der Ausführung einer Funktion gelten.
    Da der Dateinamen aber länger gebraucht wird sollte er also außerhalb einer Funktion definiert sein - also global.

    Wenn zu der Datei noch mehr Informationen kommen würden die du auch als Variable brauchst (z.B. letzter Zugriff usw.) dann könnte man es noch, um es ein bisschen aufzuräumen, als DllStruct verpacken.
    Dann hat man eine Variable mit mehreren Informationen, also quasi eine Art Dateiobjekt.

  • Seh ich auch so. Ein kompletter Verzicht auf global deklarierte Variablen mag zwar technisch umsetzbar sein, aber das erhöht eher die Risiken die durch den Einsatz globaler Variablen entstehen würden.

    Grundsätzlich ist es eher wichtig sich genau Gedanken über die Benennung und den Zweck der Variablen zu machen. Braucht man eine Variable nur in einer Funktion ist das Thema klar, lokale Variable und gut. Wird sie nur in wenigen ausgewählten Funktionen benötigt sollte man sofern der Aufwand dafür nicht zu groß ist ebenfalls lokale Variablen verwenden die jeweils weitergereicht werden. Gerade in Bezug auf GUI Elemente ist das aber in der Regel nicht praktikabel, da du so ggf. eine große Menge an Parametern weiterreichen musst. Eine Lösung wäre hier dann der Einsatz von Arrays, das würde die Übergabe vereinfachen, dafür aber die Lesbarkeit des Codes erschweren. Bei solchen Sammelarrays sollte also am besten eine gute Dokumentation im Quellcode in jeder Funktion die darauf zugreift vorliegen. Wenn die Variable hingegen in sehr vielen oder allen Funktionen benötigt wird ist es per Definition eben eine globale Variable. Hier ist dann höchste Vorsicht geboten, da je nach Programmgröße eben schwer nachvollziehbar ist wer berechtigter oder ggf. auch unberechtigter Weise, also versehentlich den Inhalt der Variable geändert hat.

    Am allerwichtigsten ist aber nicht die Frage "global ja oder nein?", sondern "deklariert ja oder nein?". Eine einzige Variable die als lokale gedacht war aber nicht deklariert wurde kann mit einer gleichnamigen globalen kolidieren. Daher sollte man sich zum einen niemals auf das "Dim" Statement verlassen und natürlich auch wirklich jede Variable explizit local oder global deklarieren.

    Hilfreich ist hier auch der Einsatz von

    [autoit]

    AutoItSetOption("MustDeclareVars", 1)

    [/autoit]
  • Hallo!

    Danke mal für die Infos, dann werd ich es vorerst mal so belassen, es stört mich so gesehen eh nicht, ich war nur neugrierg und will eben sofern einfach möglich nicht alles globalisieren.

    Der Vollständigkeit halber, das wäre der Teil der das Form baut und die Listener-Endlosschleife.


    [autoit][/autoit][autoit][/autoit][autoit]

    ; ...
    ; ~ option explicit
    AutoItSetOption('MustDeclareVars', 1)
    ; ...
    ; div
    global $globFullFilePath = "" ; global, weiß nicht wie ich den sonst in btn_recheck_click() reinbekomme
    ; ...
    ; Hauptfenster erstellen
    Func frmMain_load()
    ; Fenster
    global $hFrmMain = GUICreate($APP_TITLE, 1100, 620, -1, 50, $WS_SIZEBOX + $WS_MAXIMIZEBOX + $WS_MINIMIZEBOX, $WS_EX_ACCEPTFILES)
    ; Text/Editbox
    global $hTextBox = GUICtrlCreateEdit('', 15, 15, 1068, 520, $WS_HSCROLL + $WS_VSCROLL + $ES_READONLY)
    GUICtrlSetResizing($hTextBox, $GUI_DOCKBORDERS) ; Resize-Optionen - fix an den Rahmen ausrichten
    GUICtrlSetFont($hTextBox, 9, 400, 0, "Courier New") ; Schriftart Textbox
    GuiCtrlSetState(-1, $GUI_DROPACCEPTED) ; Drag/Drop erlauben
    ; Buttons
    local $valign = 552
    local $height = 28
    global $hBtnOpen = GUICtrlCreateButton("Datei öffnen", 480, $valign, 120, $height)
    GUICtrlSetResizing($hBtnOpen, $GUI_DOCKBOTTOM + $GUI_DOCKRIGHT + $GUI_DOCKSIZE)
    GUICtrlSetState($hBtnOpen, $GUI_DEFBUTTON) ; Default-Button
    global $hBtnReCheck = GUICtrlCreateButton("Datei erneut prüfen", 615, $valign, 120, $height)
    GUICtrlSetResizing($hBtnReCheck, $GUI_DOCKBOTTOM + $GUI_DOCKRIGHT + $GUI_DOCKSIZE)
    GUICtrlSetState($hBtnReCheck, $GUI_DISABLE) ; Initial deaktiviert
    global $hBtnClipBoard = GUICtrlCreateButton("Inhalt in die Zwischenablage kopieren", 750, $valign, 210, $height)
    GUICtrlSetResizing($hBtnClipBoard, $GUI_DOCKBOTTOM + $GUI_DOCKRIGHT + $GUI_DOCKSIZE)
    global $hBtnClose = GUICtrlCreateButton("Beenden", 974, $valign, 110, $height)
    GUICtrlSetResizing($hBtnClose, $GUI_DOCKBOTTOM + $GUI_DOCKRIGHT + $GUI_DOCKSIZE)
    ; Funktion MY_WM_GETMINMAXINFO überwacht Mindestgröße des Fensters
    GUIRegisterMsg($WM_GETMINMAXINFO, "MY_WM_GETMINMAXINFO")
    ; GUI darstellen
    GUISetState(@SW_SHOW, $hFrmMain)
    ; experimentell
    hideTextCursor()
    openFileDialog()
    ; Event Listener
    While 1
    ; experimentell
    hideTextCursor()
    Switch GUIGetMsg()
    ; Button "Datei öffnen"
    Case $hBtnOpen
    btn_open_click()
    ; Button "Datei erneut prüfen"
    Case $hBtnReCheck
    btn_recheck_click()
    ; Button "Zwischenablage"
    Case $hBtnClipBoard
    btn_clip_click($hTextBox)
    ; "Beenden" Button bzw. Event
    Case $hBtnClose, $GUI_EVENT_CLOSE
    btn_and_event_close_click()
    ; Datei via Drag and Drop auf Textbox
    Case $GUI_EVENT_DROPPED
    file_dropped(@GUI_DragFile)
    EndSwitch
    WEnd
    EndFunc

    [/autoit]


    Achja, meine Controls (Elemente) sind alle absichtlich global, das soll so sein. Mir ging es um $globFullFilePath.

    Danke mal an alle.

  • Wo ist das Problem diese Variable als lokale Variable zu nutzen und an deine andere Funktion weiterzureichen? Machbar ist es aufjedenfall, ob es sinnvoll ist ist ein anderes Thema, das hängt davon ab wo du diese überall sonst noch brauchst.

  • Bei mir sieht es gekürzt ca. so aus...

    [autoit]


    Func frmMain_load()

    global $hBtnOpen = GUICtrlCreateButton("Datei öffnen", ...)
    global $hBtnReCheck = GUICtrlCreateButton("Datei erneut prüfen", ... )
    ; Event Listener
    While 1
    Switch GUIGetMsg()

    [/autoit][autoit][/autoit][autoit]

    ; Button "Datei öffnen"
    Case $hBtnOpen
    openFileDialog()
    ; Button "Datei erneut prüfen"
    Case $hBtnReCheck
    btn_recheck_click()

    [/autoit][autoit][/autoit][autoit]

    EndSwitch
    WEnd
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    ; Dateiname ermitteln und Validierung starten
    Func openFileDialog()
    local $oldPath = $globFullFilePath ; Pfad backup
    $globFullFilePath = FileOpenDialog('Datei öffnen', @DesktopDir & "", "Text (*.csv; *.txt)|All (*.*)", $FD_FILEMUSTEXIST, '', $hFrmMain)
    If @error Then
    return False
    EndIf
    ; Pfad ok, dann Datei prüfen
    validateFile()
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    ; Button "Datei erneut prüfen"
    Func btn_recheck_click()
    validateFile()
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    Func validateFile()
    ; hier ne Menge Validierungszeugs....
    EndFunc

    [/autoit]

    Ich glaube ich verstehe wie ich den Pfad dann "wieder" ins frmMain zurückbekommen würde, aber wenn ich mir das durchdenke, dann werd ich es vorerst wirklich global lassen, sehe in der ständigen Durchreicherei keinen Vorteil - derzeit.