Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Estoy hecho un lío con las excepciones (https://www.clubdelphi.com/foros/showthread.php?t=62258)

noob 13-12-2008 04:00:08

Estoy hecho un lío con las excepciones
 
Tengo este bloque de código:

Código Delphi [-]
procedure TForm1.Button1Click(Sender: TObject);
begin
  if (Edit1.Text = '') or (Edit2.Text = '') or (Edit3.Text = '') then
    MessageBox( 0, 'Hay campos en blanco', 'Error', 0 )
  else
    MessageBox( 0, 'Todo está correcto', 'Información', 0 );
end;

Pero me gustaría gestionarlo mediante excepciones, la verdad es que me lío bastante con el try except end y es que tampoco le veo la utilidad si puedo usar un if else.

¿Me podéis decir como puedo transformar mi bloque de código en otro gestionado mediante excepciones?

Muchas gracias.

Delphius 13-12-2008 04:31:10

Hola noob,
Ese código no tiene ninguna excepción que tratar. No al menos que yo vea.
¿Que es lo que deseas hacer? es la pregunta aqui. Creo que sería fundamental que te explicases mejor sobre tu caso.

No veo un posible uso de una captura de excepción en dicho código, al menos que por la parte SI deba ejecutarse algo más que ese simple cuadro de díalogo. ¿Qué debería hacerse en caso de que no sean vacíos?

La cuestión es que el que un TEdit esté o no vacío no es motivo para una excepción. Si puede ser motivo a una excepción lo que pueda llegar a hacerse con el contenido de dichos TEdits.

Una excepción no es más que una clase heredada de TObject que sirve para dar forma al manejo de ciertos errores.

El tema no es tan fácil de comprender y explicarlo, al menos para mi que llevo unos minutos tratando de ordenar ideas para exponerlas. Te invito a que leas el capítulo 11 del libro La Cara Oculta de Delphi 4. Allí está tratada hermosamente el tema. Está a disposición de los miembros del club una copia pdf de dicho libro (su autor lo ha cedido al público para su libre distribución) en el FTP del club.

De forma breve cuando uno esta previendo el tema de una excepción y el control de la misma es porque en alguna parte del código escrito existe el riesgo de que una excepción pueda arrojarse.
Cuando se presenta, estamos hablando ya no de un caso de éxito dentro del ámbito normal y esperado. Algo ha sucedido y por tanto ahora nos hemos desviados del curso normal y esperado. Es responsabilidad nuestra el determinar que hacer en esta situación:

1. Simplemente no hacer nada y propragar la excepción. Lo que implica que el mensaje sea enviado al usuario y por tanto que exista el peligro de dejar a la aplicación en un punto inestable.
2. Capturar la excepción y ver el modo de revertir el problema con el fin de llevar al sistema a un punto estable.
3. Capturar la excepción y simplemente ignorarla por no saber como tratarla. No informando del defecto o problema al usuario.

Cuando uno hace uso de métodos, funciones, procedimientos, asignaciones, y/o eventos en delphi está firmando cierto "contrato" como dice Ian en su libro que te recomendé. Como parte del contrato, se te hace saber las clásulas. Que harás tu con ellas, allí tu. Esta clásula es la documentación que acompaña al método, función, procedimiento, asignaciones, eventos.

En la ayuda se indica que puede y no puede hacerse. En caso de que se haya diseñado el algoritmo para que notifique de los problemas mediante una excepción, en dicha documentación se hará conocer el tipo de la excepción.

Nuevamente disculpa por no poder ser muy claro en esto. En dicho documento está tratado el tema.

Cuanto más puedas comentarnos sobre tu duda más fácil será para nosotros poder ayudarte.

Saludos,

Lepe 13-12-2008 07:37:44

Yo lo tengo muy claro, aunque puede que mi método sea discutible ;).

creo que todo se reduce a:
- querer abortar la ejecución del programa.

Si quieres abortar, usa una excepción.
Si no, usa un MessageBox.

En tu caso no es necesario una excepción porque el código del botón no hace mucho código.

Hay veces que el código es muy complejo, y anidar los "if" junto con los MessageBox para que todo funcione correctamente hace que el código se complique, en estos casos usar una excepción puede ser más simple y funcional.

Un ejemplo didáctico:
Código Delphi [-]
type eRequerido = class(Exception);
//creo un tipo de excepción propia
...
procedure TForm1.GuardaValores;
msg := '';
for i:= 0 to tabla1.fields.count -1 do
  if tabla1.fields[i].required and tabla1.fields[i].isnull then
    msg := msg + 'campo ' + tabla.fields[i].displayName + ' es requerido' +#13#10;
if msg <> '' then
 raise eRequerido.Create(' Se necesitan valores en los siguientes campos:' + msg);
tabla1.Post;
CalcularBaseImponible;

end;

Procedure TForm1.CalculaTotal:
begin 
  try 
    GuardaValores;
  except on exception : eRequerido do
     statusbar1.text := 'No se puede calcular el total de la factura, faltan datos';
    raise;
 end;
end;
Se podría incluir un MessageBox en lugar de una excepción, pero como digo, esto es un ejemplo simple, se puede complicar más las cosas.

Llamamos a CalculaTotal:
Lo que hago es bien sencillo, primero miro si algún campo requerido está en blanco, si es así, lanzo una excepción propia con un mensaje indicando exactamente los campos vacíos que el usuario debe rellenar y en este punto, se corta le ejecución del programa, es decir, las instrucciones que están más abajo no se ejecutarán.

Pero aún hay algo más: al producirse una excepción se vuelve hacia atrás en la pila de llamadas ejecutadas, así que volverá a la rutina "CalculaTotal" y allí encuentra un bloque try ... except. Puesto que la excepción lanzada por mí es una eRequerido y coincide con la que hay escrita en el bloque try... except, se ejecuta el código, colocando un mensaje en el StatusBar. y ahora se obliga a relanzar la excepción producida con la instrucción raise; (ten en cuenta que hasta ahora no se ha mostrado el mensaje de error, de hecho los try...excepts están para intentar corregir el error y dar la posibilidad de que el programa continúe con normalidad), por tanto, se vuelve a hacia atrás en la pila de llamadas, pero ahora, desde la rutina CalculaTotal hacia atrás...

Si al volver hacia atrás en la pila de llamadas no encuentra ningún try..except llegará al nivel más alto que es el Application.OnExceptions (desde el componente TApplications de delphi puedes usarlo). Si no tienes un TApplications en tu programa, la excepción se muestra al usuario.

Si hubiesemos usado un MessageBox, en la rutina CalculaTotal no sabríamos si se ha guardado la factura o no, tendríamos que echar mano de una bandera (variable booleana que la la ponemos a true o false dependiendo si ha habido error o no al guardar) y con estas cosas se nos empieza a complicar el código, en legibilidad, claridad, etc.

Como bien dices, ya que el botón lo único que hace es chequear si todo está correcto o no, no tiene sentido lanzar una excepción. La excepción se lanza para que no ocurra un mal mayor.

Espero haya sido claro.... que va a ser que no ;)

Saludos

Lepe 13-12-2008 07:45:35

Cita:

Empezado por Delphius (Mensaje 330742)
3. Capturar la excepción y simplemente ignorarla por no saber como tratarla. No informando del defecto o problema al usuario.

Sólo comentar que esa opción tiene mucho peligro:
- el programa no hace lo que se espera
- no sabes que ha ocurrido un error.
- no sabes cómo arreglarlo.

Si no sabes cómo arreglarlo, no lo toques :p... quiero decir, es mejor ver la excepción en pantalla o enviarlo a un archivo .log que ignorarla definitivamente. Si la VCL ha lanzado una excepción... por algo será ;)

Saludos

Delphius 13-12-2008 14:01:48

Cita:

Empezado por Lepe (Mensaje 330747)

creo que todo se reduce a:
- querer abortar la ejecución del programa.

Si quieres abortar, usa una excepción.
Si no, usa un MessageBox.

Me gusta esa descripción simple: para abortar.

Cita:

Empezado por Lepe (Mensaje 330748)
Sólo comentar que esa opción tiene mucho peligro:
- el programa no hace lo que se espera
- no sabes que ha ocurrido un error.
- no sabes cómo arreglarlo.

Si no sabes cómo arreglarlo, no lo toques :p... quiero decir, es mejor ver la excepción en pantalla o enviarlo a un archivo .log que ignorarla definitivamente. Si la VCL ha lanzado una excepción... por algo será ;)

Saludos

Bueno amigo, yo dije opciones:D. No es una buena elección como bien lo mencionas, pero en fin de que se puede optar por esa opción se puede.;)

Por cierto. Si se hace uso de un Log de excepciones lo mejor es que sea un singleton. Es decir, que exista un sólo punto en donde se traten a las excepciones. Todas las excepciones van a un mismo objeto TLog, por ejemplo, y son guardadas en un único archivo.

Aunque en ocasiones, no es buena opción. Más si tiene pensando en trabajar con muchos archivos .log.

De igual manera, en ocasiones ante casos de éstas excepciones raras y a las que no sabemos tratar lo mejor es tener un "cuadro de diálogo único" por donde comunicarlas.

Lo que si es una buena opción a tener en cuenta es la de convertir excepciones a un grado de abstracción adecuado y entendible para la clase que la recibe. Por ejemplo, supongamos que tenemos las clase A, B y C. A para trabajar se comunica con B, y B con C. A cuenta con un método MA, y en el cual se procede a una comunicación lineal hasta C. Es decir, MA invoca a un método MB, y MB a uno de MC. Ahora MC lanza una excepción EC, que la captura MB. Esta excepción a A no le corresponde el adecuado grado de abstracción, por tanto B "convierte" a esa excepción EC a una excepción EB, añadiendole la información necesaria para que A sepa como tratarla y se la transmite a A.

Espero que me entienda,

Saludos,

noob 13-12-2008 15:53:53

Sí mi código es demasiado simplón para que pueda ser reescrito mediante excepciones.
Pongo otro:

Código Delphi [-]
MySQL50Connection1.Open;

MySQL50Connection1 es un componente que se conecta a un servidor MySQL.

Si todas las propiedades de ese componente (HostName, Port, UserName, Password, DatabaseName) están bien establecidas entonces se conectará al servidor MySQL sin problemas pero si no lo están, por ejemplo no hemos equivocado al introducir la contraseña el programa cascará y la ejecución se detendrá.

Lo que yo quiero es controlar este aspecto y en vez de que casque que se envíe un mensaje al usuario pero que la ejecución no se detenga.

Según lo que habéis escrito pienso que podría ser algo así:

Código Delphi [-]
try
  MySQL50Connection1.Open; // inténtate conectar al servidor MySQL
except
  ShowMessage('No he podido conectarme'); // en caso excepcional, si no puedes conectarte dímelo
end;

Pienso que algo falta porque no he usado raise ni tampoco he hecho except on.

¿Me ayudáis a completarlo?

Saludos.

ElKurgan 13-12-2008 17:27:03

Primeramente, estoy con el amigo Delphius: El capítulo mencionado del libro de la cara oculta es la mejor forma de entender el como y el porqué de las excepcines.

Por lo demás, si lo que quieres es mostrar un mensaje entendible sobre lo que ha sucedido, con lo que has puesto en tu código te vale perfectamente. Si lo que qieres es mostrar el mensaje pero además que el propio sistema te gestione la excepción original bastaría con añadir la clausula "Raise" después de showmessage, para que la excepción siga propagádose a lo largo del programa, hasta el gestor final.

Un saludo

ContraVeneno 13-12-2008 17:29:28

Código Delphi [-]
try
 MySQL50Connection1.Open; // inténtate conectar al servidor MySQL 
except 
 on E:Exception do begin
  ShowMessage('No he podido conectarme debido a: ' E.ClassName + ' - '+ E.Message); // en caso excepcional, si no puedes conectarte dímelo 
 end; //except
end; //try


Eso sería muuuuy general y poco recomendable, ya que podría pasar cualquier cosa y aparecería el mismo mensaje. Lo mejor es utilizar una clase de excepción y una acción específica para cada cosa:

Código Delphi [-]
 try
 MySQL50Connection1.Open; // inténtate conectar al servidor MySQL 
except 
 on E:EDBEngine do begin
  //manejo en caso de error de base de datos
 end; //DNEngine
 On E:EAccessViolation do begin
  //Manejo de error en acceso de memoria
 end; //AccessViolation
end; //try

Lepe 13-12-2008 17:32:18

Un detallito... Tu código no es simplón, es que testea una serie de condiciones y actua en todos los casos posibles (con un MessageBox, pero hace algo), por eso quizás no he entendido tu objetivo principal.

Pensando de forma global (abstracta que dirían algunos) tu último ejemplo está bien, ya que se trata de saber si se puede conectar o no con MySql.

Si quieres obtener información detallada de por qué no se ha podido conectar, tendrás que mirar los tipos de excepciones que lanza MySql y detectar el código de cada uno, a partir de ahí personalizar los mensajes.

Si quieres detalles, usa on except y el tipo de excepción de MySql. Lo mismo es aplicable para el trabajo de excepciones en general, siempre puedes usar un código general (para todos los tipos de excepciones) y otro más concreto para excepciones con un código de error.

Lo más rápido para ver el tipo de error producido es:
Código Delphi [-]
try
  MySQL50Connection1.Open; // inténtate conectar al servidor MySQL
except 
  on e:exception do
    ShowMessage(e.ClassName); // en caso excepcional, si no puedes conectarte dímelo
end;

A ver si un compañero que trabaje con MySql puede ponerte un código de ejemplo.

Saludos

noob 13-12-2008 17:36:27

La idea es que se gestione la excepcion pero que no muestre ningún mensaje al usuario, es decir, quiero que el programa se intente conectar y si no puede que no muestre ningún mensaje más que nada porque el programa que estoy haciendo va a ser un servidor y no va a haber ningún usuario para que cierre ventanas.

Algo así:

Código Delphi [-]
try
  MySQL50Connection1.Open; // inténtate conectar al servidor MySQL
except 
  Fallo := true // en caso excepcional, si no puedes conectarte dímelo
end;

ContraVeneno 13-12-2008 18:05:16

con más razón necesitas saber cuál es la clase de la excepción cuando falla la conexión.

Dejar sin clase la excepción te podría generar más problemas de los que resuelve.

noob 13-12-2008 18:11:44

De todas las propiedades por las que puede fallar: HostName, Port, UserName, Password, DatabaseName, un error de conexión sólo puede deberse a que la contraseña o el nombre de usuario no estén correctos, así que el error siempre se va a deber a eso.

Antes, cuando no usaba excepciones me salía en un mensaje que la clase de la excepción era EDatabaseError.

¿Cómo puedo saber los tipos de excepciones que lanza MySql?

Saludos.

enecumene 14-12-2008 15:14:28

Cita:

Empezado por noob (Mensaje 330778)
De todas las propiedades por las que puede fallar:
¿Cómo puedo saber los tipos de excepciones que lanza MySql?

¿Qué componentes usas para conectarte a MySQL?, ya que los componentes tienen sus propias excepciones para manejarlas de acuerdo al tipo de Base de Datos, ya sean ZEOS, MyDAC, etc..

En el caso de ZEOS la exception que maneja las conecciones es el EZSQLException.

Saludos.

noob 14-12-2008 15:19:54

Uso estos componentes:
TMySQL50Connection
TSQLTransaction
TSQLQuery

¿Les conocéis?

enecumene 14-12-2008 15:22:11

¿BDE? :confused:

noob 14-12-2008 15:24:51

Cita:

Empezado por enecumene (Mensaje 330827)
¿BDE? :confused:

¿Qué es BDE?

enecumene 14-12-2008 15:26:43

Cita:

Empezado por noob (Mensaje 330828)
¿Qué es BDE?

:eek:, Son componentes de conección de Bases de Datos, lo trae Delphi por defecto, en fin, ¿cómo se llaman esos componentes que usas, su nombre general?.

Saludos.

noob 14-12-2008 15:29:49

Se llaman así como te digo, vienen por defecto en Lazarus, es un IDE open source calcado a Delphi.


La franja horaria es GMT +2. Ahora son las 09:11:24.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi