Delphi-PRAXiS
Seite 1 von 5  1 23     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Sinnvoller Einsatz von goto (https://www.delphipraxis.net/149370-sinnvoller-einsatz-von-goto.html)

Luckie 19. Mär 2010 23:36


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:
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;
Ich stelle das hier einfach mal zur Diskussion.

Christian Seehase 20. Mär 2010 00:00

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:
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;
BTW:
Den Speicher in der Funktion freizugeben, halte ich nicht für sinnvoll, da er ja dort nicht belegt wird.

Luckie 20. Mär 2010 00:06

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.

Neutral General 20. Mär 2010 00:37

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;

Luckie 20. Mär 2010 00:47

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.

Neutral General 20. Mär 2010 00:53

Re: Sinnvoller Einsatz von goto
 
Der wird noch abgearbeitet. Ist mir auch erst vor kurzem eher rein zufällig beim Debuggen aufgefallen ;)

Luckie 20. Mär 2010 00:55

Re: Sinnvoller Einsatz von goto
 
Das try-finally gefällt mir sogar besser, weil da sicher gestellt wird, dass der Speicher wieder freigegeben wird.

Sir Rufo 20. Mär 2010 01:05

Re: Sinnvoller Einsatz von goto
 
Zitat:

Zitat von Neutral General
Der wird noch abgearbeitet. Ist mir auch erst vor kurzem eher rein zufällig beim Debuggen aufgefallen ;)

Hätte mich aber auch gewundert, wenn nicht.

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:

Medium 20. Mär 2010 02:18

Re: Sinnvoller Einsatz von goto
 
Zitat:

Zitat von Sir Rufo
- internationaler Weltuntergang

Nationale wären nicht so schlimm? :stupid:

alzaimar 20. Mär 2010 06:39

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:
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;
Hmm. Das mit der Schleife und dem Abort-Flag geht ja nun mal gar nicht (Abort ist ein reserviertes Schlüsselwort):
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:
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;
Hier eine Alternative ohne Exceptions, sondern mit einer Funktion, die den Erfolg als Rückgabewert liefert.
Delphi-Quellcode:
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;
Das ist -finde ich- noch lesbarer.
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:
..
  If Action1IsSuccessfull then
    If Action2IsSuccessfull then
      If Action3IsSuccessfull then
        DoSomething
      else
        HandleException
    else
      HandleException
  else
    HandleException
..
Dann lieber so (Try-Except-Paradigma)
Delphi-Quellcode:
...
Try
  Action1;
  Action2;
  Action3;
Except
  HandleException;
End;
Paradigmen sollten zwar stringent durchgezogen werden, aber wozu sind solche Regeln da, wenn nicht zum brechen? :mrgreen:


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:15 Uhr.
Seite 1 von 5  1 23     Letzte »    

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz