Les meilleures pratiques pour faire imbriquée try / déclaration ENFIN
-
29-08-2019 - |
Question
Salut Quelle est la meilleure façon de le faire essayer emboîtée & finally dans delphi?
var cds1 : TClientDataSet;
cds2 : TClientDataSet;
cds3 : TClientDataSet;
cds4 : TClientDataSet;
begin
cds1 := TClientDataSet.Create(application );
try
cds2 := TClientDataSet.Create(application );
try
cds3 := TClientDataSet.Create(application );
try
cds4 := TClientDataSet.Create(application );
try
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
finally
cds4.free;
end;
finally
cds3.free;
end;
finally
cds2.free;
end;
finally
cds1.free;
end;
end;
Pouvez-vous suggérer une meilleure façon de le faire?
La solution
comment sur les points suivants:
var cds1 : TClientDataSet;
cds2 : TClientDataSet;
cds3 : TClientDataSet;
cds4 : TClientDataSet;
begin
cds1 := Nil;
cds2 := Nil;
cds3 := Nil;
cds4 := Nil;
try
cds1 := TClientDataSet.Create(nil);
cds2 := TClientDataSet.Create(nil);
cds3 := TClientDataSet.Create(nil);
cds4 := TClientDataSet.Create(nil);
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
finally
freeandnil(cds4);
freeandnil(cds3);
freeandnil(cds2);
freeandnil(Cds1);
end;
end;
maintient compact et tente seulement de libérer les instances qui ont été créées. Il n'y a vraiment pas besoin d'effectuer l'imbrication puisque tout échec entraînera la chute et enfin effectuer toutes le nettoyage dans l'exemple que vous avez fourni.
Personnellement, j'essaie de ne pas se nicher dans le même méthode ... à l'exception d'un essai / try / except / scénario finalement. Si je me trouve avoir besoin de nid, alors pour moi qui est un grand temps de penser refactorisation dans un autre appel de méthode.
EDIT Nettoyé un peu grâce aux commentaires par mghie et utku .
EDIT a changé la création d'un objet pour ne pas faire référence à l'application, comme pas nécessaire dans cet exemple.
Autres conseils
J'utiliser quelque chose comme ceci:
var
Safe: IObjectSafe;
cds1 : TClientDataSet;
cds2 : TClientDataSet;
cds3 : TClientDataSet;
cds4 : TClientDataSet;
begin
Safe := ObjectSafe;
cds1 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
cds2 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
cds3 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
cds4 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
// if Safe goes out of scope it will be freed and in turn free all guarded objects
end;
Pour la mise en œuvre de l'interface voir cet article , mais vous pouvez facilement créer quelque chose de similaire vous .
EDIT:
Je viens de remarquer que, dans la garde d'article lié () est une procédure. Dans mon code, j'ai surchargé les fonctions qui renvoient TObject, au-dessus exemple de code Guard () suppose quelque chose de similaire. Bien sûr, avec beaucoup génériques meilleur code est maintenant possible ...
EDIT 2:
Si vous vous demandez pourquoi essayer ... est finalement complètement retiré dans mon code: Il est impossible de supprimer les blocs imbriqués sans introduire la possibilité de fuites de mémoire (lorsque des exceptions soulèvent Destructeurs) ou violations d'accès. Par conséquent, il est préférable d'utiliser une classe d'aide, et laisser le comptage de référence des interfaces prendre en charge complètement. La classe d'aide peut libérer tous les objets qu'il gardes, même si certains des exceptions Destructeurs soulèvent.
Il y a une autre variante du code sans essayer imbriqué ... enfin que vient de se produire pour moi. Si vous ne créez pas les composants avec le paramètre AOwner du constructeur mis à zéro, alors vous pouvez simplement utiliser la gestion de la vie que la VCL vous donne gratuitement:
var
cds1: TClientDataSet;
cds2: TClientDataSet;
cds3: TClientDataSet;
cds4: TClientDataSet;
begin
cds1 := TClientDataSet.Create(nil);
try
// let cds1 own the other components so they need not be freed manually
cds2 := TClientDataSet.Create(cds1);
cds3 := TClientDataSet.Create(cds1);
cds4 := TClientDataSet.Create(cds1);
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
finally
cds1.Free;
end;
end;
Je suis un grand croyant dans un petit code (si elle est pas trop brouillées).
Si vous voulez aller dans cette (OMI) la route laide (manipulation avec initialisation groupe à zéro pour savoir si est nécessaire libération), vous au moins doit garantir que vous ne laisserez pas une exception dans l'un des destructor empêcher de libérer le reste de vos objets.
Quelque chose comme:
function SafeFreeAndNil(AnObject: TObject): Boolean;
begin
try
FreeAndNil(AnObject);
Result := True;
except
Result := False;
end;
end;
var cds1 : TClientDataSet;
cds2 : TClientDataSet;
IsOK1 : Boolean;
IsOK2 : Boolean;
begin
cds1 := Nil;
cds2 := Nil;
try
cds1 := TClientDataSet.Create(nil);
cds2 := TClientDataSet.Create(nil);
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
finally
IsOk2 := SafeFreeAndNil(cds2); // an error in freeing cds2 won't stop execution
IsOK1 := SafeFreeAndNil(Cds1);
if not(IsOk1 and IsOk2) then
raise EWhatever....
end;
end;
Il y a une bonne vidéo sur dans les constructeurs et destructeurs
Il montre quelques beaux exemples tels que:
var cds1 : TClientDataSet;
cds2 : TClientDataSet;
begin
cds1 := Nil;
cds2 := Nil;
try
cds1 := TClientDataSet.Create(nil);
cds2 := TClientDataSet.Create(nil);
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
finally
freeandnil(cds2); //// what has if there in an error in the destructor of cds2
freeandnil(Cds1);
end;
end;
Ce qui a s'il y a une erreur dans la destructor de cds2
Cds1 ne sera pas détruit
EDIT
Une autre bonne ressource est:
Jim McKeeth excellente vidéo sur Delayed de traitement des exceptions dans la plage de codes III ont été, il parle de problèmes à la gestion des exceptions dans le bloc finally.
@mghie: Delphi a obtenu empiler des objets alloués:
type
TMyObject = object
private
FSomeField: PInteger;
public
constructor Init;
destructor Done; override;
end;
constructor TMyObject.Init;
begin
inherited Init;
New(FSomeField);
end;
destructor TMyObject.Done;
begin
Dispose(FSomeField);
inherited Done;
end;
var
MyObject: TMyObject;
begin
MyObject.Init;
/// ...
end;
Malheureusement, comme l'exemple ci-dessus montre:. Les objets attribués Stack n'empêchent pas les fuites de mémoire
Donc, ce serait encore besoin d'un appel à la destructor comme ceci:
var
MyObject: TMyObject;
begin
MyObject.Init;
try
/// ...
finally
MyObject.Done;
end;
end;
OK, je l'avoue, cela est presque hors sujet, mais je pense qu'il pourrait être intéressant dans ce contexte car la pile des objets attribués ont été cités comme une solution (qui ne sont pas s'il n'y a pas d'appel de destructor automatique).