Pourquoi exception dans l'utilisation de manipulation apparemment code « sûr »?

StackOverflow https://stackoverflow.com/questions/1894983

  •  19-09-2019
  •  | 
  •  

Question

S'il vous plaît, peut quelqu'un me expliquer, ce qui peut soulever une exception dans ce code?

function CreateBibleNames: TStrings;
begin
  Result := TStringList.Create;
  try
    Result.Add('Adam');
    Result.Add('Eva');
    Result.Add('Kain');
    Result.Add('Abel');
  except
    Result.Free;
    raise;
  end;      
end;

Depuis que je l'utilise delphi je l'ai utilisé la gestion des exceptions peut-être une fois. Je considère que le code ci-dessus pour être écrit par un programmeur skillfull et je ne pense pas que les exceptions sont redondantes. Mais encore, en utilisant la gestion des exceptions dans ce concept reste un mystère pour moi. Il semble être un code de sécurité (sans essayer sauf fin). Je l'ai vu plusieurs fois des extraits de code similaires comme celui-ci, c'est pourquoi il y a probablement une bonne raison de l'écrire de cette façon, en dépit de mon expérience, qui ne prouve pas qu'il est nécessaire.

De plus en cas d'échec quelque chose, je reçois la description d'exception ....

Thanx

Était-ce utile?

La solution

D'accord, ce code est étrange, je suis d'accord, et je comprends tout à fait pourquoi il a obtenu écrit de cette façon. Mais il a été écrit de cette façon parce que la prémisse sous-jacente du code est erroné. Le fait que la construction semble étrange devrait être une « odeur de code », et devrait vous dire que quelque chose pourrait ne pas être se fait de la meilleure façon possible.

Tout d'abord, voici pourquoi la construction inhabituelle dans le bloc try ... except. La fonction crée un TStringList, mais si quelque chose se passe mal au cours de le remplir, le TStringList créé sera « perdu » sur le tas et sera une fuite de mémoire. Ainsi, le programmeur d'origine était défensive et fait en sorte que si une exception a eu lieu, le TStringList serait libéré et l'exception se sont soulevées à nouveau.

Maintenant, voici la partie « mauvaise prémisse ». La fonction retourne une instance de TStrings. Ce n'est pas toujours la meilleure façon d'aller à ce sujet. De retour d'une instance d'un objet comme ça pose la question: « Qui va se débarrasser de cette chose que j'ai créé? ». Il crée une situation où il pourrait être facile - du côté appelant - oublier qu'une instance TStrings a été alloué.

Une « meilleure pratique » ici est d'avoir la fonction prendre un TStrings en tant que paramètre, puis remplir l'instance existante. De cette façon, il n'y a aucun doute sur qui est propriétaire de l'instance (l'appelant) et donc qui doit gérer sa vie.

Ainsi, la fonction devient une procédure et pourrait ressembler à ceci:

procedure CreateBibleNames(aStrings: TStrings);
begin
  if aStrings <> nil then
  begin
    aStrings .Add('Adam');
    aStrings .Add('Eva');
    aStrings .Add('Kain');
    aStrings .Add('Abel');
  end;      
end;

Maintenant cela ne veut pas dire que le retour d'une instance d'objet est une mauvaise chose à chaque fois - qui est la seule fonction du modèle d'usine très utile, par exemple. Mais pour plus de fonctions « générales » comme celui-ci, ce qui précède est une « meilleure » façon de faire les choses.

Autres conseils

Il est une bonne habitude d'être quand le retour des objets nouvellement construits à partir des fonctions. Dans un scénario plus complexe qu'une liste de chaînes, certains invariant peut être cassé ou une autre erreur se produit, et donc une exception, et dans ce cas, vous voulez la valeur de retour libéré. Plutôt que d'avoir à examiner toutes les situations possibles quand cela peut se produire, il est généralement une bonne pratique d'avoir un modèle unique pour le retour des objets nouvellement construits et de le suivre partout.

De cette façon, lorsque la mise en œuvre des objets que vous construisez des changements dans l'avenir, vous êtes toujours en sécurité si elles arrivent à jeter une exception.

La seule raison possible pour une exception, je peux voir est une exception OutOfMemory et si tel est le cas - tous les paris sont hors de toute façon. À mon avis, c'est un exemple d'utilisation abusive d'un concept valide.

L'utilisation excessive et injustifiée d'essayer, sauf le code et encombre le rend plus difficile à lire

Je voudrais écrire le code de cette façon, pour plusieurs raisons:

1) En standardisant la façon dont les allocations de mémoire regarder, il est facile d'inspecter le code source pour voir si quelque chose manque, sans avoir à lire toutes les lignes. Ici, vous voyez juste le .Create, et notez que la gestion des exceptions est bon, donc vous ne devez pas lire la partie entre try ... except.

2) En standardisant la façon dont vous les allocations de mémoire de code, vous ne jamais oublier de le faire, même en cas de besoin.

3) Si vous modifier ultérieurement le code source, insérez un code, changer la façon dont fonctionne .Add, remplacez TStringList avec quelque chose d'autre, etc., alors il ne serait pas briser ce code.

4) Cela signifie que vous ne devez pas penser à si TStringList.Add () va lancer des exceptions, ou non, soulager votre cerveau pour d'autres travaux qui fournit plus de valeur.

L'exception la plus évidente serait ce que Delphi utilise pour un OutOfMemoryException. S'il n'y a pas assez de mémoire pour ajouter des chaînes couple à une liste, libérer toute la liste et re-jeter l'exception.

Cela semble une programmation trop défensive pour faire une liste de noms prédéterminés -. En règle générale, si vous obtenez une exception en raison d'être de mémoire, votre application entière est lavé au jet

Sans la source TStringList, ou la documentation faisant autorité, vous ne pouvez pas vraiment savoir. Juste à titre d'exemple, supposons que TStringList est écrit de telle sorte que lorsque la mémoire devient serré, il commence à échanger des parties de la liste et à partir du disque, ce qui vous permet de gérer une liste aussi importante que votre disque. Maintenant, l'appelant est également sensible à toute la gamme des erreurs d'E / S:. D'espace disque, bloc défectueux rencontré, délai d'attente du réseau, etc

est le traitement de ces exceptions surpuissant? jugement fondé sur le scénario. On pourrait demander aux programmeurs de la NASA ce qu'ils pensent.

Ce genre de code est un modèle « d'usine » classique. Cet exemple est trivial, et ne peut pas exiger la gestion des exceptions, car il pourrait soulever des exceptions dans les cas extrêmes d'angle. Mais si le code d'usine est beaucoup plus complexe, il est correct de libérer l'instance créée avant re-soulever une exception rencontrée parce que l'appelant ne peut pas savoir si l'instance a été créée ou non avant l'exception élevé. Il est préférable de prendre l'instance n'a pas été créé ou libéré si une exception est renvoyée. Les usines sont utiles lorsque l'instance de retour ne peut pas être toujours le même (à savoir une classe dans une hiérarchie donnée), et lorsque les paramètres pour créer l'instance sont « unknonwn » à l'appelant, mais pas l'usine. Dans ce cas, l'appelant ne peut pas passer une instance déjà créé juste pour être « rempli » avec les paramètres appropriés, mais a juste besoin d'une instance - bien sûr, il doit être clair que l'appelant devient le « propriétaire » de l'instance créée .

En tant que note intéressante, certains des auteurs de Java considèrent maintenant la décision d'exiger la saisie d'une exception dans presque tous les cas, être une erreur. En effet, la plupart des exceptions résultent vraiment dans le programme tués, alors vous pourriez aussi bien laisser le système d'exécution tuer le programme et imprimer une trace de la pile, plutôt que de forcer le programmeur à intercepter l'exception et ... eh bien, juste imprimer quelque chose et tuer le programme.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top