![]() |
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; |
Re: Sinnvoller Einsatz von goto
N.B. Und vielleicht auch noch die 64-Bit-Version vorbereiten:
Delphi-Quellcode:
Mist - zereditiert
{$IF (CompilerVersion<20.00)}
Type NativeInt = type Integer; //Delphi2007: SizeOf(NativeInt)<>4(!) {$IFEND} ... SendMessage(ParentHandle, CM_STATUS, NativeInt(PChar('Durchlauf:')), i); ... |
Re: Sinnvoller Einsatz von goto
Zitat:
Integer bleibt 32 bittig, wenn ich richtig informiert bin .... |
Re: Sinnvoller Einsatz von goto
Das hat dann aber nicht mehr soooo viel mit GOTO zu tun, dem Thema des Threads.
|
Re: Sinnvoller Einsatz von goto
Zitat:
|
Re: Sinnvoller Einsatz von goto
Zitat:
Zitat:
|
Re: Sinnvoller Einsatz von goto
Delphi-Quellcode:
function DemoThread(p: Pointer): Integer;
var i: Integer; ParentHandle: THandle; begin ParentHandle := PThreadParams(p)^.ParentHandle; for i := 0 to 9 do begin if Abort then begin SendMessage(ParentHandle, CM_ABORT, 0 , 0); Break; end; SendMessage(ParentHandle, CM_STATUS, Integer(PChar('Durchlauf:')), i); Sleep(500); if i = 9 then SendMessage(ParentHandle, CM_FINISHED, 0, 0); end; Dispose(p); Result := 0; end;
Delphi-Quellcode:
Wobei bei Sowas das Try-Finally, bezüglich des der Speicherfeigabe, sowieso in irgendeiner Weise zu bevorzugen ist.
function DemoThread(p: Pointer): Integer;
var i: Integer; ParentHandle: THandle; begin ParentHandle := PThreadParams(p)^.ParentHandle; i := 0 while (i <= 9) and not Abort do begin SendMessage(ParentHandle, CM_STATUS, LPARAM(PChar('Durchlauf:')), i); Sleep(500); Inc(i); end; if i > 9 then SendMessage(ParentHandle, CM_FINISHED, 0, 0) else SendMessage(ParentHandle, CM_ABORT, 0 , 0); Dispose(p); Result := 0; end;
Delphi-Quellcode:
Abgesehn davon ist der Quatsch mit dem 32-Bit-Integer in 64 Bt hier egal, denn selbst wenn man hier den Pointer ordnungsgemäß in einen 64-Bit-Intreger (in einem 64-Bit-Compiler) umwandelt, dann muß man eh erstmal abgleichen ob dort SendMessage auch 64-Bit-Parameter hat ... also denke ich mal, daß man dort auch gleich den Typen des Parameters nutzen kann.
function DemoThread(p: Pointer): Integer;
var i: Integer; ParentHandle: THandle; begin try ParentHandle := PThreadParams(p)^.ParentHandle; i := 0 while (i <= 9) and not Abort do begin SendMessage(ParentHandle, CM_STATUS, LPARAM(PChar('Durchlauf:')), i); Sleep(500); Inc(i); end; if i > 9 then SendMessage(ParentHandle, CM_FINISHED, 0, 0) else SendMessage(ParentHandle, CM_ABORT, 0 , 0); finally Dispose(p); end; Result := 0; end; Und ja, ich finde auch, daß es an einigen Stellen einfach einfacher mit GOTO lösbar ist. PS: ein Break, While, For, Case und vieles Anderes besteht intern quasi auch nur ein ein paar IFs+GOTO. :angel: |
Re: Sinnvoller Einsatz von goto
Zitat:
Zitat:
|
Re: Sinnvoller Einsatz von goto
![]() Tut mir ja Leid, aber ohne Goto hätte ich dort so Einiges an doppeltem Code und/oder die eine oder andere Stelle hätte etwas umständlicher gelößt werden müssen. Und nein, bis auf wenige Stellen nutze ich auch sehr selten ein Goto, also ich kann nicht behaupten, daß ich dieses auf Krampf versuche immer zu verwenden ... wie man oben sieht, geht es auch ja auch oftmals anders/ohne. Aber bei Rücksprüngen oder beim Rumspringen in mehreren verschachtelten Schleifen ist es schon hier oder da auch mal recht gut zu gebrauchen. Und wozu soll man etwas Vorhandenes und vorallem Funktionierendes nicht verwenden, wenn es damit "einfacher" geht? Wenn man weiß, was man macht, dann erzeugt ein Goto auch keinen gefährlichen Code. |
Re: Sinnvoller Einsatz von goto
Da reichen vier Buchstaben: Nein
|
Re: Sinnvoller Einsatz von goto
Zitat:
Ich bin auch äußerst verwundert über diesen Thread. Gerade von Luckie hätte ich das nicht erwartet :gruebel: Sinnvoller Einsatz von goto ... ist nicht möglich. |
Re: Sinnvoller Einsatz von goto
Hi,
auch mein Senf dazu: ich habe noch keinen Fall gesehen indem man goto verwenden müsste! Auch noch so mühsam konstruierte Fälle kann man mit den üblichen Konstrukten abfangen. Ansonsten: Alle Jahre wieder kommt das "goto" Kind! Grüsse Rainer |
Re: Sinnvoller Einsatz von goto
Zitat:
Zitat:
Zitat:
"Wenn ich weiss, was ich tue, mache ich keine Fehler." Der Satz ist so banal wie: "Wenn es regnet, wird die Erde naß". Den kann man nicht verneinen. Zitat:
Zitat:
Zitat:
In diesem Thread geht es nicht darum zu zeigen, wie man ein GOTO vermeidet (ich unterstelle Michael mal, das er weiss, wie das geht), sondern um die Frage, ob Code mit einem GOTO nicht manchmal lesbarer wird! Der zweite Versuch von Dir ist ein Paradebeispiel für Codeencryption :mrgreen: .
Delphi-Quellcode:
Ich verwende diese While-Schleife immer wieder gerne als Beispiel, wieso ein Sprung (Break, Exit, und die Verwendung einer eigenen Funktion) sinnvoller ist, als das umständliche Hantieren mit Schleifen und Abbruchbedingungen.
...
while (i <= 9) and not Abort do ... Zitat:
Ich behaupte aber mal als Konsequenz (um auch eine Brücke zu omata zu schlagen): 1. Jedes einigermaßen nachvollziehbare GOTO springt aus einer Schleife heraus. Andere sinnvolle Verwendungen gibt es nicht. 2. Jede Schleife kann in eine Funktion (SRP) verfrachtet werden, wodurch das GOTO durch ein EXIT ersetzt werden kann. 3. Ergo kann man GOTO streichen, ohne die Lesbarkeit des Codes zu verschlechtern. 4. Im Umkehrschluss kann Code lesbarer werden, wenn kein GOTO verwendet wird. (4) Gilt aber nicht automatisch und immer, wie man an den vielen Alternativen hier gesehen hat! So, und wer nun kein Anhänger des 'Clean Code' ist und nicht immer Bock hat, jede Pupsschleife in eine eigene Funktion zu packen, nur um ein GOTO zu vermeiden, der soll in Gottes Namen sein GOTO verwenden. Davon geht die Welt nicht unter. Der Code bleibt effizient und vor allem wesentlich lesbarer, als jeder krampfhafte Versuch, mit Flags und While-Schleifen ein GOTO zu vermeiden. |
Re: Sinnvoller Einsatz von goto
Noch mal eine Kleinigkeit, die ggf. gegen ein Goto spricht: Da es nicht in allen Sprachen existiert (z.B. in Java), lässt sich ein Quelltext mit Goto schlechter in solche Sprachen portieren, weil man den Code dazu verstehen (was natürlich immer von Vorteil ist) und auch ändern muss :lol:
(Klar, euch kann es egal sein, ob andere Programmierer die einen guten Code gerne auf "ihre" Sprache portieren möchten sich dazu abplagen müssen oder nicht ...) |
Re: Sinnvoller Einsatz von goto
Wikipedia meint:
Zitat:
Lösung quadratischer Gleichungen mit der Hochzeitsformel, wenn a 0 ist.
Delphi-Quellcode:
Dann löst das 'mal komplizierter ...
VAR A,B,C : Integer;
Label : Ende; Begin Anweisungen ... Readln(A); If (A=0) Then Goto Ende; Anweisungen ... Ende: {Hier ist das Label Ende} Writeln('Hier ist wirklich Schluß'); End. |
Re: Sinnvoller Einsatz von goto
Zitat:
Delphi-Quellcode:
:?:
VAR A,B,C : Integer;
Begin Anweisungen ... Readln(A); If A <> 0 then begin Anweisungen ... end; Writeln('Hier ist wirklich Schluß'); End. Oder wie oder was??? |
Re: Sinnvoller Einsatz von goto
IIRC gab es in TP < 7 weder break noch exit, daher waren gotos die einzige Möglichkeit, aus einer Schleife herauszuspringen. Ansonsten stört mich weder break, exit noch goto, wenn es sinnvoll und spärlich eingesetzt wird und der Lesbarkeit dient. So, die ewig Gestrigen können mich jetzt meinetwegen steinigen.
[edit] Kleine Korrektur: eine Möglichkeit gab es doch mit dem Einsatz einer Boolschen Variablen, die gesetzt werden konnte, um die Schleife zu verlassen, aber das war doch ziemlich umständlich IMO [/edit] |
Re: Sinnvoller Einsatz von goto
Programmieren ist eine Tätigkeit, die schöpferisch und sehr individuell von Statten geht.
Jeder hat Vorlieben oder Aversionen auf Grund seiner Erfahrung, was die Codierung betrifft. Und niemand muss sich zu irgendwelchen "Programmier-Stil-Standards" zwingen lassen. Also soll doch bitteschön niemand meckern, wenn jemand in bestimmten Situation auch zu "verpönten" Befehlen wie Goto, Exit, Break, Halt usw. greift. Die Hauptsache ist, dass das Programm fehlerfrei läuft. Den Anwender interessiert es herzlich wenig, was im Quell-Code steht. :-D |
Re: Sinnvoller Einsatz von goto
Zitat:
|
Re: Sinnvoller Einsatz von goto
Zitat:
Ist dort festgelegt, dass ein "goto" nicht verwendet werden darf, ist man gezwungen, dieses auch nicht einzusetzen. |
Re: Sinnvoller Einsatz von goto
Zitat:
Und dass ein Arbeitgeber vorgibt, in welchem Stil programmiert werden muss, hab ich noch nie gehört. Es gibt meist Vorgaben für die Software (was sie können muss, wie sie aussehen und gestaltet sein soll etc.). Dem Auftraggeber ist es meist sogar egal, in welcher Sprache gearbeitet wird. Hauptsache seine Vorgaben werden erfüllt. Wir machen Software, die verkauft werden soll und nicht, welche um den besten Programmierstil wetteifert. Das intrweressiert den Käufer nicht die Bohne *g* |
Re: Sinnvoller Einsatz von goto
Zitat:
Dort gibt es oftmals entsprechende Vorgaben, damit der Code innerhalb der Firma "gleich" aussieht und damit natürlich auch alle den Code dann besser verstehen können. > also meist existiert da ein entsprechender Style-Guide |
Re: Sinnvoller Einsatz von goto
Vor allem ist "Auftraggeber" nicht unbedingt gleich "Arbeitgeber"!
Dem Auftraggeber kann der Code und die verwendete Programmiersprache egal sein. Dem Arbeitgeber aber noch lange nicht! |
Re: Sinnvoller Einsatz von goto
Zitat:
Zitat:
Zitat:
|
Re: Sinnvoller Einsatz von goto
Äh, merkt denn hier keiner, dass über den Schnee von vor 40 Jahren diskutiert wird ? :shock: Alle Jahre wieder oder was ? :zwinker: Ich vermute mal, dass das Goto lediglich im Ur-Pascal vorhanden war, damit Umsteiger von Basic nicht direkt an neueren Schleifen etc. scheitern. That's it. Exit gabs übrigens schon vor BP 7. Hatte das auch gern eingesetzt. Irgendwann hatte ich dann aber gemerkt, dass es mehr ärgerliche Quereffekte verursacht, als es wert ist. Was heisst nun sinnvoll ? IMHO, auf diesen uralten goto-Firlefanz grundsätzlich zu verzichten. 8-) Warum soll man etwas mit goto machen, nur weil es auch gehen würde ? Vorteile hat es jedenfalls keine. Ausser, sich darüber zu streiten, was nun lesbarer ist ? Wenn mir ein Beispiel-Quelltext auf den Tisch käme, der mehr als 0 gotos beinhaltet, dann womöglich direkt -> Tonne. :mrgreen:
|
Re: Sinnvoller Einsatz von goto
Zitat:
Daher ist eben für mich dieses "Programmieren nach Vorschrift" irrelevant ;) |
Re: Sinnvoller Einsatz von goto
Auch wenn es langsam offtopic wird, aber nur damit ich das richtig verstehe. Du schreibst jedes Jahr das komplette Programm neu, wenn der Kunde etwas geändert oder erweitert haben will? Kannst du mir mal bitte sagen, was das soll und warum du das machst?
|
Re: Sinnvoller Einsatz von goto
Zitat:
Und dass ein Kunde (das wäre in diesem Fall z.B. ein MediaMarkt-Kunde, der das Teil für 9,99 gekauft hat) etwas geändert haben wollte, ist noch nie vorgekommen. Wäre ja auch irgendwie absurd *g* Zumal die Programme unter dem Namen unseres Auftraggebers erscheinen und unsere Firma da eigentlich unerwähnt ist. |
Re: Sinnvoller Einsatz von goto
Wenn es um die Lesbarkeit geht ist folgende Variante, angelehnt ans Cristian Seehase´s Version noch lesbarer.
Delphi-Quellcode:
Jeder soll programmieren wie er selber meint den Code am Besten lesen zu können, denn in 80% der Fälle wird der Verfasser auch den Code lesen.
function DemoThread(const AParentHandle : THandle): Integer;
var i: Integer; begin for i := 0 to 9 do begin if Abort then begin SendAbort(AParentHandle ); break; end else begin SendStatus(AParentHandle, i); Sleep(500); end; end; if not Abort then begin SendFinished(AParentHandle); Result := 0; end; end; procedure SendAbort(const AParentHandle : THandle); begin SendMessage(AParentHandle, CM_ABORT, 0 , 0); end; procedure SendStatus(const AParentHandle : THandle; index: Integer); begin SendMessage(AParentHandle, CM_STATUS, Integer(PChar('Durchlauf:')), index); end; procedure SendFinished(const AParentHandle : THandle); begin SendMessage(AParentHandle, CM_FINISHED, 0, 0); end; |
Re: Sinnvoller Einsatz von goto
Zitat:
|
Re: Sinnvoller Einsatz von goto
Noch etwas Öl oder Code für's Feuer. :stupid:
Also ich habe bei dieser Funktion noch keinen Weg gefunden, es ohne Goto elegant zu formulieren.
Delphi-Quellcode:
// Prüft, ob eine Datei für den Import vorhanden ist.
// Falls nicht, kann der Benutzer entscheiden, was passieren soll function CheckImportFile(const fname: TFilename; const Caption:string): Boolean; var msg: string; label again; begin again: if not FileExists(fname) then msg := Format('Datei <%s> ist nicht vorhanden', [fname]) else if FileGetSize(fname) = 0 then msg := Format('Datei <%s> ist leer', [fname]) else msg := ''; if msg <> '' then begin case MessageBox(0, PChar(msg), PChar(Caption), MB_ICONWARNING or MB_ABORTRETRYIGNORE or MB_SETFOREGROUND) of idAbort: Abort; // stille Exception idRetry: goto again; idIgnore: begin Result := False; // Datei vorhanden oder leer; der Benutzer möchte die Datei ignorieren Exit; end; end; end; Result := True; // Datei vorhanden und nicht leer end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:41 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