![]() |
Sinnvoller Einsatz von goto
Der Einsatz von goto wird ja immer so verteufelt. Ich habe das bisher eigentlich schon immer etwas differenziert gesehen, hatte bisher nur nie die Situation, in der ein goto sinnvoll gewesen wäre. Aber ich denke, gerade eben bin ich über einen sinnvollen Einsatz gestolpert:
Delphi-Quellcode:
Ich stelle das hier einfach mal zur Diskussion.
function DemoThread(p: Pointer): Integer;
var i: Integer; ParentHandle: THandle; label CLEANUP; begin ParentHandle := PThreadParams(p)^.ParentHandle; for i := 0 to 9 do begin if Abort then begin SendMessage(ParentHandle, CM_ABORT, 0 , 0); goto CLEANUP; end; SendMessage(ParentHandle, CM_STATUS, Integer(PChar('Durchlauf:')), i); Sleep(500); end; SendMessage(ParentHandle, CM_FINISHED, 0, 0); CLEANUP: Dispose(p); Result := 0; end; |
Re: Sinnvoller Einsatz von goto
Moin Michael,
ja, da könnte man wohl goto verwenden, aber mir gefällt folgende Lösung besser:
Delphi-Quellcode:
BTW:
function DemoThread(const AParentHandle : THandle): Integer;
var i: Integer; begin for i := 0 to 9 do begin if Abort then begin SendMessage(AParentHandle, CM_ABORT, 0 , 0); break; end else begin SendMessage(AParentHandle, CM_STATUS, Integer(PChar('Durchlauf:')), i); Sleep(500); end; end; if not Abort then SendMessage(AParentHandle, CM_FINISHED, 0, 0); Result := 0; end; Den Speicher in der Funktion freizugeben, halte ich nicht für sinnvoll, da er ja dort nicht belegt wird. |
Re: Sinnvoller Einsatz von goto
Das wäre natürlich eine Lösung. Allerdings finde ich meine besser lesbar.
Wollte ich den Speicher nicht in der Thread-Funktion frei geben, müsste ich die Variable global machen und doch wieder an anderer Stelle im Code freigeben. So hat man den direkten Bezug, auch wenn es natürlich oftmals schöner ist den Speicher dort wieder frei zu geben, wo man ihn anfordert. Da stimme ich dir zu. |
Re: Sinnvoller Einsatz von goto
Nunja ein try-finally wäre wohl die richtige Wahl in dieser Situation ;)
Delphi-Quellcode:
function DemoThread(p: Pointer): Integer;
var i: Integer; ParentHandle: THandle; begin try ParentHandle := PThreadParams(p)^.ParentHandle; for i := 0 to 9 do begin if Abort then begin SendMessage(ParentHandle, CM_ABORT, 0 , 0); exit; end; SendMessage(ParentHandle, CM_STATUS, Integer(PChar('Durchlauf:')), i); Sleep(500); end; SendMessage(ParentHandle, CM_FINISHED, 0, 0); finally Dispose(p); Result := 0; end; end; |
Re: Sinnvoller Einsatz von goto
Stellt sich die Frage, ob nach dem Exit der finally-Abschnitt noch abgearbeitet wird. Wenn dem so ist, dann spricht natürlich nichts gegen deine Lösung.
|
Re: Sinnvoller Einsatz von goto
Der wird noch abgearbeitet. Ist mir auch erst vor kurzem eher rein zufällig beim Debuggen aufgefallen ;)
|
Re: Sinnvoller Einsatz von goto
Das try-finally gefällt mir sogar besser, weil da sicher gestellt wird, dass der Speicher wieder freigegeben wird.
|
Re: Sinnvoller Einsatz von goto
Zitat:
War try...finally nicht so definiert, dass der finally-Teil in jedem Fall abgearbeitet wird, egal was zwischen try und finally passiert? Gut es gibt da schon Ausnahmen, die aber von dieser Betrachtung ausgeschlossen werden können: - Stromausfall - internationaler Weltuntergang - etc. um nur einige zu nennen :mrgreen: |
Re: Sinnvoller Einsatz von goto
Zitat:
|
Re: Sinnvoller Einsatz von goto
Da die Funktion zwei Aufgaben erfüllt (sende Nachrichten und räume auf), teilen wir sie. Da ein Goto zu 99.9% der Fälle eine Aufgabe beendet, fällt das Goto nun automatisch weg. Das ist kein Trick, sondern sowieso Pflicht, um das SRP (Single Responsibility Princip) auf funktionaler Ebene einzuhalten.
Delphi-Quellcode:
Hmm. Das mit der Schleife und dem Abort-Flag geht ja nun mal gar nicht (Abort ist ein reserviertes Schlüsselwort):
procedure TryHardToSendMessages (aParentHandle : THandle);
var i: Integer; begin for i := 0 to 9 do begin if Abort then begin SendMessage(ParentHandle, CM_ABORT, 0 , 0); exit; end; SendMessage(ParentHandle, CM_STATUS, Integer(PChar('Durchlauf:')), i); Sleep(500); end; SendMessage(ParentHandle, CM_FINISHED, 0, 0); End; function DemoThread(p: Pointer): Integer; begin Try TryHardToSendMessages (PThreadParams(p)^.ParentHandle); Finally Dispose(p); End; Result := 0; end; Außerdem und überhaupt ist nicht so richtig klar, wie die Nachrichten verwendet werden sollen. Mal sehen: Wir senden 10x eine Nachricht 'CM_STATUS'. Nach jeder Nachricht warten wir 500ms. Wenn alle 10 Nachrichten verschickt haben, senden wir ein 'CM_FINISHED'. Wurden wir ausnahmsweise (<-Zaunpfahl) unterbrochen, senden wir ein CM_ABORT.
Delphi-Quellcode:
Hier eine Alternative ohne Exceptions, sondern mit einer Funktion, die den Erfolg als Rückgabewert liefert.
Procedure TryHardToSendMessages (aParentHandle : THandle);
var i: Integer; begin Try for i := 0 to 9 do if ExternalAbort then abort else begin SendMessage(ParentHandle, CM_STATUS, Integer(PChar('Durchlauf:')), i); Sleep(500); end SendMessage(ParentHandle, CM_FINISHED, 0, 0); Except // TODO: Vollständige Exceptionbehandlung SendMessage(ParentHandle, CM_ABORT, 0 , 0); end; end;
Delphi-Quellcode:
Das ist -finde ich- noch lesbarer.
Function Sending10MessagesWasNotAborted(aParentHandle) : Boolean;
Var i : Integer; Begin for i := 0 to 9 do if ExternalAbort then break else begin SendMessage(ParentHandle, CM_STATUS, Integer(PChar('Durchlauf:')), i); Sleep(500); end; Result := ExternalAbort; End; Procedure TryHardToSendMessages (aParentHandle : THandle); begin If Sending10MessagesWasNotAborted(aParentHandle) Then SendMessage(ParentHandle, CM_FINISHED, 0, 0) else SendMessage(ParentHandle, CM_ABORT, 0 , 0); end; Nachteil der 2.Variante bzw. des dort verwendeten Verfahrens: Wenn dieses Paradigma durchgezogen wird, entstehen schnell verschachtelte und unleserliche If-Then-Abläufe, z.B.:
Delphi-Quellcode:
Dann lieber so (Try-Except-Paradigma)
..
If Action1IsSuccessfull then If Action2IsSuccessfull then If Action3IsSuccessfull then DoSomething else HandleException else HandleException else HandleException ..
Delphi-Quellcode:
Paradigmen sollten zwar stringent durchgezogen werden, aber wozu sind solche Regeln da, wenn nicht zum brechen? :mrgreen:
...
Try Action1; Action2; Action3; Except HandleException; End; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:21 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz