GARANTIR DESCONTO

Fórum raise: quando usar e quando não usar #330609

03/10/2006

0

as vezes eu uso um bloco try... except, e aí, depois de tratado o erro,
eu mostro uma mensagem customizada por mim e talvez ainda concatene com a mensagem do exception. Tipo assim:

try
..
..
except
on e: exception do
begin
//trata
//trata
messagebox(0, ´bla´, pchar(´blablabla´+e.message), 0);
end;
end;


só que hoje eu tive que ´encapsular´ alguns desses try.. except dentro de métodos de outras classes.

A minha pergunta é: devo usar raise exception.create(mensagem) ao inves de messagebox(mensagem) nas minhas classes? ou Os metodos devem retornar true ou false caso houve sucesso ou não? Como eu pegaria a mensagem de erro nese caso: ´um metodo XX da classe AA instancia um objeto da classe BB e executa um método qualquer, mas na execução do método da classe BB ocorre uma excessão. ´

Se ocorre uma exception a execução do porgrama para? como eu posso fazer pra configurar se vai mostrar mensagem ou gerar exception?

se eu fizer algo do tipo:

procedure RaiseOrShow(msg: string; Reraise: Boolean = True);
begin
  if  Reraise then
    raise Exception.Create(msg)
  else
    MessageBox(0, ´Erro!´, PChar(msg), ADV_ALERTA);
end;


dá certo?


Grato!


Vitor Rubio

Vitor Rubio

Responder

Posts

03/10/2006

Tnaires

Olá

Tudo isso depende do comportamento do seu método. Acredito que a maneira mais elegante seja colocar um raise Exception.Create(´Msg´) dentro dos métodos, e ´protegê-los´ com try / except no ambiente de execução. Exemplo:
procedure MeuMetodo;
begin
  if CoisaRuim then
    raise Exception.Create(´Coisa ruim.´);
end;

procedure AmbienteExecucaoMeuMetodo;
begin
  try
    MeuMetodo;
  except
    Application.MessageBox(PChar(E.Message), ´Erro´, MB_OK + MB_ICONERROR);
  end;
end;

Inclusive, um tratamento genérico de exceções pode ser escrito para o evento OnException do Application, ou utilizando um AppEvents.

Mas e se você tiver alguma função que precise retornar algo, independendo das condições de erro? Nesse caso, o try / except vai pra dentro da função. O bloco except atribui ao Result um valor válido, mas que simbolize erro dentro do contexto do programa ( -1, por exemplo ).

Abraços


Responder

Gostei + 0

03/10/2006

Michael

Em geral é preferível usar exceções para notificar erros, do que valores de retorno. Exceções são auto-descritivas - ou pelo menos deveriam ser - , podem carregar mais informações sobre o problema e garantem que caso um erro ocorra ele deverá ser tratado, pois caso contrário o aplicativo abortará (em Java, por exemplo, o compilador obriga vc a tratar qualquer método que levante uma exceção, usando try..catch). Veja um exemplo:

TConection = class
public
  function Connect: Boolean;
end;

...
begin
  Connection.Connect;
  Connection.ExecQuery(...);
  ...
end;


Se a conexão falhar, vai ocorrer outro erro na linha abaixo pq eu não tratei o retorno do método. Se uma exceção fosse levantada de dentro de [b:c1efa33f78]Connect[/b:c1efa33f78], nada após seria executado.

Outro ponto: se vc utilizar valores de retorno em cada método, vai precisar usar um tipo mais flexivel do que [b:c1efa33f78]Boolean[/b:c1efa33f78], pq dificilmente a quantidade de erros passíveis de ocorrem será binária. E não é recomendável ´agrupar´ erros em um único significado, pois isso dificulta a localização do ponto exato do problema. Usando ainda o método [b:c1efa33f78]Connect [/b:c1efa33f78]da classe mostrada acima como exemplo, se ele retornar [b:c1efa33f78]False[/b:c1efa33f78], como será possível saber se o problema é pq a senha do usuário está incorreta, pq o banco informado não existe, ou pq simplesmente a rede está offline?

Nessas situações, costuma-se usar [b:c1efa33f78]Integer [/b:c1efa33f78]como tipos de retorno. Porém, em paralelo, vc vai ter que montar tabelas de códigos de erro, para poder saber o que um 1, 2 ou -1 significam. Isso não é bom, e tende a tornar a manutenção do código mais trabalhosa, pois o significado de cada código não será o mesmo em todos os métodos: 1 é uma coisa para o método Foo1, e outra para o método Foo2, etc.

Por estas e outras razões que eu recomendo utilizar exceções para notificar erros na aplicação.

[]´s


Responder

Gostei + 0

05/10/2006

Vitor Rubio

Valeu pelas respostas, pessoal!

O meu caso é o seguinte: antes eu tratava a excessão dentro do método
e retornava, para quem o chamou, true ou false, para saber se houve erro ou não. Mas a exception era gravada num log e, as vezes, tambem era mostrada para o usuario num messagebox. Só que agora é diferente....
agora a minha classe não vai mais ser usada diretamente pela interface, mas vai ser ´encapsulada´ por um método de uma outra classe. Não sei se eu trato a exception da mesma maneira e se retornar false eu ´reraiso´ essa exception, ou se eu simplesmente mostro messagebox ou se eu deixo todo o tratamento na interface. Desse jeito eu nunca vou saber que erro que ocoreu, é verdade. Mas se eu deixar pra tratar tudo na interface, vou ter muitos códigos parcialmente repetidos para tratamento dessas excessões. Não sei uma outra forma de saber, mostrar e logar, na parte mais externa, o que aconteceu de errado na parte mais interna.


Responder

Gostei + 0

05/10/2006

Tnaires

Não daria certo você escrever uma rotina genérica de tratamento de exceções e atribuí-la ao evento OnException do Application, ou mesmo a um AppEvents?

Dessa forma, você trata as exceções em um canto só, sem repetição de código, e sem usar Application.MessageBox em classes que não deveriam tratar de janelas e interfaces com o usuário.


Responder

Gostei + 0

05/10/2006

Vitor Rubio

o problema é o seguinte:
minhas classes instanciam objetos de outras classes, em varios niveis.

se ocorrer uma excesão num método e eu tratar e mostrar um messagebox, beleza, depois disso o metodo continua.

Mas se eu propagar a mensagem de erro já tratada, com uma mensagem minha, criando oexception, tipo raise exception.create(´minha mensagem de erro´) a aplicação trava aí, e não prossegue.

eu preciso que a aplicação prossiga, para poder dar um free nos objetos instanciados.

veja o exemplo:

procedure TForm1.Button1Click(Sender: TObject);
var objeto : TStringList;
begin
  objeto := TStringList.Create;
  raise Exception.Create(´teste´);
  ShowMessage(´testou?´);
  objeto.Free;
end;


o objeto não vai ser liberado nunca, e isso ocorre em varios lugares. Alem disso, eu tenho log de erro por e-mail, por arquivo texto e por banco de dados...


Responder

Gostei + 0

05/10/2006

Tnaires

Para liberar os objetos, use [b:c9ec1ef0b9]finally[/b:c9ec1ef0b9].

Exemplo: suponha que você tem um método particular que gere uma exceção:

TClasse = class
  public
    procedure MeuMetodo;
  end;

procedure TClasse.MeuMetodo;
begin
  raise Exception.Create(´Exceção criada.´);
end;


A rotina genérica de erro pode tratar essa exceção, e gravá-la no log sem problemas, pois ela recebe uma referência à própria exceção como parâmetro ( vide evento OnException do componente AppEvents ). Para resolver o problema de liberação de objetos, faça:

with TClasse.Create do
  try
    MeuMetodo;
  finally
    Free;
  end;


Nesse caso, o objeto sempre será liberado após a execução do método, independente de haver exceção ou não.


Responder

Gostei + 0

06/10/2006

Vitor Rubio

Mas considere o código que você mesmo criou:

TClasse = class 
  public 
    procedure MeuMetodo; 
  end; 

procedure TClasse.MeuMetodo; 
var AlgumaCoisa: ToutraClasse;
begin 
  AlgumaCoisa := ToutraClasse.create;
  AlgumaCoisa.FazOQueTemQueFazer;
  If CoisaRuimAconteceu then
    raise Exception.Create(´Exceção criada.´); 
  AlgumaCoisa.Free; //nunca vai entrar nessa linha  
end;



with TClasse.Create do 
  try 
    MeuMetodo; 
  finally 
    Free; //não adianta dar free aqui, porque o objeto "AlgumaCoisa" não vai ser liberado
  end;



Eu uso exceptions para, alem de mostrar as mensagens de algo ruim para o usuario, criar um log, mas eu não posso criar exceptions para coisas boas, esse é um problema. Outro problema é o seguinte: se eu criar uma função assim:

procedure RaiseOrShow(msg: string; Reraise: Boolean = True);
begin
  if  Reraise then
    raise Exception.Create(msg)
  else
    MessageBox(0, PChar(msg), ´Erro!´ , ADV_ALERTA);
end;


eu queria saber se é correto, quando for mensagem boa fazer:

raisorshow(´mesnagem de sucesso´, false);


e quando for ruim fazer:

RaiseOrShow(´iih, Ferrô!´, true);


dá certo? o raise para no lugar certo? onde ele deverá ser tratado?


Responder

Gostei + 0

06/10/2006

Tnaires

Beleza.

No contra-exemplo que você citou, um finally viria a calhar:

TClasse = class
  public
    procedure MeuMetodo;
  end;

procedure TClasse.MeuMetodo;
var AlgumaCoisa: ToutraClasse;
begin
  AlgumaCoisa := ToutraClasse.create;
  try
    AlgumaCoisa.FazOQueTemQueFazer;
    If CoisaRuimAconteceu then
      raise Exception.Create(´Exceção criada.´);
  finally
    AlgumaCoisa.Free; //Essa linha sempre será executada
  end;
end;


A regra é: try/finally sempre deve ser usado quando um objeto que executa um método passível de exceção precisar ser liberado imediatamente após sua execução.

Sobre o RaiseOrShow, eu afirmo o seguinte: exceptions só devem ser utilizadas quando houver algum erro crítico no sistema, que possa comprometer sua execução. Para avisos, informações gerais, ´mensagens boas´ e etc, não use exceções. Seguindo essa afirmação, o RaiseOnShow perde todo o sentido.


Responder

Gostei + 0

06/10/2006

Vitor Rubio

Era isso que eu precisava saber! Eu não sabia que você podia colocar
try.. finally numa exception que você mesmo ´raisou´

quanto ao RaiseOrShow, é que eu queria usar a mesma maneira de mostrar uma mensagem, idependente de acontecer coisa ruim ou boa, então o raiseorshow mostraria mensagem caso acontecesse coisa boa ou geraria exception no caso de coisa ruim.

tipo assim:

RaiseorShow(mensagem, (a=b));

mas eh meio estranho, né?


Responder

Gostei + 0

06/10/2006

Vitor Rubio

tnaires, olha só esse teste que eu fiz:

procedure TForm1.Button1Click(Sender: TObject);
var objeto : TStringList;
begin
  try
    objeto := TStringList.Create;
    Exit;
    raise Exception.Create(´teste´);
  finally
    ShowMessage(´testou?´);
    ShowMessage(´testou mesmo´);
    objeto.Free;
  end;
end;


O finally executa mesmo depois de um exit!!! :D:D:D:D

e eu não sabia disso! Era tudo o que eu queria. Muito obrigado, você me ajudou muito!!!


Responder

Gostei + 0

06/10/2006

Tnaires

Beleza, qualquer coisa estamos aí!


Responder

Gostei + 0

06/10/2006

Vitor Rubio

apareceu outra duvida sobre exceptions:
eu estou usando o raise para propagar exceptions caousadas dentro de classes minhas para serem tratadas na interface com o usuario.
as vezes as exceptions não são geradas pelos meus objetos, mas sim por objetos usados por eles.

Então no exemplo:

try
  MeuObjetoQueMandaEmail.Envia; //se der exception aqui, provavelmente é senha invalida ou coisa do tipo, e foi o idSmtp que gerou.
except
  {a minha duvida á aqui: eu uso raise exception.create(´deu erro´) ou só raise?}
  raise;
end;


queria saber quando usar raise; e quando usar raise exception.create(´bla bla bla´), e tambem quando usar o:

on e: exception do
begin
  raise ou raise exception;
  cuida da coisa ruim;
end;



Responder

Gostei + 0

06/10/2006

Vitor Rubio

Outra coisa: Se as exceptions são objetos da classe Exception, quem é que da free nelas?


Responder

Gostei + 0

06/10/2006

Tnaires

O raise isolado dentro de uma cláusula exception serve para repassar a instância da exceção.

try
  try
    raise Exception.Create(´Coisa ruim´);
  except
    on E: Exception do
    begin
      // Faz alguma coisa e repassa a exceção
      raise;
    end;
  end;
except
  on E: Exception do
  begin
    // Trata a exceção repassada
  end;
end;


O bloco try/except externo vai receber a exceção repassada.

Por falar em on E: Exception... Esse comando serve para tratar um tipo ( classe ) de exceção específico. Exemplo:

type
  ECoisaRuim = class(Exception);

  ECoisaPiorAinda = class(Exception);

...

procedure MeuMetodo;
begin
  try
    // Este método pode gerar ECoisaRuim ou ECoisaPiorAinda
    GerarException;
  except
    on E1: ECoisaRuim do
    begin
      // Tratamento para exceções do tipo ECoisaRuim
    end;
    on E1: ECoisaPiorAinda do
    begin
      // Tratamento para exceções do tipo ECoisaPiorAinda
    end;
  end;
end;


[quote:6031bfc017=´vitor^_^´]Outra coisa: Se as exceptions são objetos da classe Exception, quem é que da free nelas?[/quote:6031bfc017]

Diretamente do help do delphi:

When an exception handler actually handles the exception, it finishes by destroying the exception instance, so you never need to do that yourself.


:wink:


Responder

Gostei + 0

07/10/2006

Vitor Rubio

Legal, eu não sabia que as exceptions no delphi tinham tanto poder de fogo assim. Estou programando errado a alguns ANOS.

e no caso do on e: exception do eu posso simplesmente usar para mudar a mensagem da exception antes de repassar ela pra frente, por exemplo:

  try 
    MetodoQuePodeGerarException;
  except 
    on E: Exception do 
    begin 
      // Faz alguma coisa e repassa a exceção 
      //Metodos que tratam o erro se necessário
      raise exception.create(´Minha mensagem personalisada com a original concatenada no final´ + #1310+ e.message); 
     //o resto vai seer tratado depois, na classe que chamou esse metodo
    end; 
  end; 



Valew pela ajuda!


Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar