try delphi try-finally try-except

delphi - Cómo escribir correctamente Try..Finally... Except statements?



delphi try except finally (6)

Tome el siguiente código como muestra:

procedure TForm1.Button1Click(Sender: TObject); var Obj: TSomeObject; begin Screen.Cursor:= crHourGlass; Obj:= TSomeObject.Create; try // do something finally Obj.Free; end; Screen.Cursor:= crDefault; end;

si ocurriera un error en la sección // do something , el TSomeObject creado, supongo, no se liberará y Screen.Cursor seguirá bloqueado como Hour Glass, porque el código estaba roto antes de llegar a esas líneas.

Ahora, a menos que esté confundiendo, debería existir una declaración de Excepción para tratar cualquier ocurrencia de un error, algo así como:

procedure TForm1.Button1Click(Sender: TObject); var Obj: TSomeObject; begin try Screen.Cursor:= crHourGlass; Obj:= TSomeObject.Create; try // do something finally Obj.Free; end; Screen.Cursor:= crDefault; except on E: Exception do begin Obj.Free; Screen.Cursor:= crDefault; ShowMessage(''There was an error: '' + E.Message); end; end;

Ahora, a menos que esté haciendo algo realmente estúpido, no debería haber ninguna razón para tener el mismo código dos veces en el bloque Finally y después, y en el bloque Exception.

Básicamente, a veces tengo algunos procedimientos que pueden ser similares a la primera muestra que publiqué, y si obtengo un error, el cursor se bloquea como un reloj de arena. Agregar los manejadores de excepciones ayuda, pero parece una manera sucia de hacerlo, básicamente ignorando el bloque Finally, sin mencionar el código feo con copy-paste de las partes de Finally to Exception.

Todavía estoy aprendiendo mucho acerca de Delphi, así que me disculpo si esto parece ser una pregunta / respuesta directa.

¿Cómo se debe escribir correctamente el código para manejar los enunciados y liberar correctamente objetos y capturar errores, etc.?


Como otros han explicado, debes proteger el cambio del cursor con try finally block. Para evitar escribir esos, uso un código como este:

unit autoCursor; interface uses Controls; type ICursor = interface(IInterface) [''{F5B4EB9C-6B74-42A3-B3DC-5068CCCBDA7A}''] end; function __SetCursor(const aCursor: TCursor): ICursor; implementation uses Forms; type TAutoCursor = class(TInterfacedObject, ICursor) private FCursor: TCursor; public constructor Create(const aCursor: TCursor); destructor Destroy; override; end; { TAutoCursor } constructor TAutoCursor.Create(const aCursor: TCursor); begin inherited Create; FCursor := Screen.Cursor; Screen.Cursor := aCursor; end; destructor TAutoCursor.Destroy; begin Screen.Cursor := FCursor; inherited; end; function __SetCursor(const aCursor: TCursor): ICursor; begin Result := TAutoCursor.Create(aCursor); end; end.

Ahora solo lo usa como

uses autoCursor; procedure TForm1.Button1Click(Sender: TObject); var Obj: TSomeObject; begin __SetCursor(crHourGlass); Obj:= TSomeObject.Create; try // do something finally Obj.Free; end; end;

y el mecanismo de interfaz contado de referencia de Delphi se encarga de restaurar el cursor.


Creo que la versión más "correcta" sería esta:

procedure TForm1.Button1Click(Sender: TObject); var Obj: TSomeObject; begin Obj := NIL; Screen.Cursor := crHourGlass; try Obj := TSomeObject.Create; // do something finally Screen.Cursor := crDefault; Obj.Free; end; end;


Después de haber hecho un montón de código en servicios / servidores que necesitan manejar excepciones y no matar la aplicación, suelo buscar algo como esto:

procedure TForm1.Button1Click(Sender: TObject); var Obj: TSomeObject; begin try Obj := NIL; try Screen.Cursor := crHourGlass; Obj := TSomeObject.Create; // do something finally Screen.Cursor := crDefault; if assigned(Obj) then FreeAndNil(Obj); end; except On E: Exception do ; // Log the exception end; end;

Tenga en cuenta el intento finalmente; dentro del intento excepto; y la colocación de la creación Obj.

si el Obj crea otras cosas dentro de su constructor, puede funcionar a mitad de camino y fallar con una excepción dentro de .create (); pero aún así ser un Obj creado. Así que me aseguro de que el Obj siempre se destruya si se ha asignado ...


Lo haría así:

var savedCursor: TCursor; Obj: TSomeObject; begin savedCursor := Screen.Cursor; Screen.Cursor := crHourGlass; Obj:= TSomeObject.Create; try try // do something except // record the exception end; finally if Assigned(Obj) then Obj.Free; Screen.Cursor := savedCursor; end; end;


Solo necesitas dos bloques try/finally :

Screen.Cursor:= crHourGlass; try Obj:= TSomeObject.Create; try // do something finally Obj.Free; end; finally Screen.Cursor:= crDefault; end;

La pauta a seguir es que debe usar finally lugar de, except para proteger recursos. Como ha observado, si intenta hacerlo con except entonces se ve obligado a escribir el código de finalización dos veces.

Una vez que ingrese el bloque try/finally , se garantiza que se ejecutará el código en la sección finally , sin importar qué ocurra entre try y finally .

Por lo tanto, en el código anterior, el try/finally externo try/finally asegura que Screen.Cursor se restaure ante cualquier excepción. Del mismo modo, el try/finally interno asegura que Obj se destruya en caso de que se Obj alguna excepción durante su vida útil.

Si desea manejar una excepción, necesita un bloque try/except distinto. Sin embargo, en la mayoría de los casos, no debe tratar de manejar excepciones. Simplemente permita que se propague al manejador principal de excepciones de la aplicación que mostrará un mensaje al usuario.

Si maneja la excepción para bajar la cadena de llamada, el código de llamada no sabrá que el código al que llamó ha fallado.


Tu código original no es tan malo como crees:

procedure TForm1.Button1Click(Sender: TObject); var Obj: TSomeObject; begin Screen.Cursor := crHourGlass; Obj := TSomeObject.Create; try // do something finally Obj.Free; end; Screen.Cursor := crDefault; end;

Obj.Free se ejecutará sin importar lo que ocurra cuando // do something . Incluso si ocurre una excepción (después de la try ), ¡se ejecutará el bloque finally ! Ese es el objetivo de la construcción de try..finally !

Pero también quieres restaurar el cursor. La forma más pedante es usar dos construcciones try..finally :

procedure TForm1.Button1Click(Sender: TObject); var Obj: TSomeObject; begin Screen.Cursor := crHourGlass; try Obj := TSomeObject.Create; try // do something finally Obj.Free; end; finally Screen.Cursor := crDefault; end; end;

[Sin embargo, tampoco me importaría

procedure TForm1.Button1Click(Sender: TObject); var Obj: TSomeObject; begin Obj := TSomeObject.Create; Screen.Cursor := crHourGlass; try // do something finally Screen.Cursor := crDefault; Obj.Free; end; end;

demasiado. El riesgo de Screen.Cursor := crHourGlass falla es bastante bajo, pero en tal caso el objeto no se liberará (el finally no se ejecutará porque no estás dentro del try ), por lo que el doble try..finally es más seguro .]