Best practice verschachtelte try / finally-Anweisung zu tun
-
29-08-2019 - |
Frage
Hallo Was ist der beste Weg, verschachtelten try & schließlich Aussagen in delphi zu tun?
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;
Können Sie eine bessere Möglichkeit, dies zu tun?
Lösung
, wie etwa die folgenden:
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;
Das hält sie kompakt und versucht, nur die Instanzen zu befreien, die erstellt wurden. Es gibt wirklich keine Notwendigkeit, die Verschachtelung durchzuführen, da jeder Ausfall in Abwurf auf das schließlich führen wird und alle der Bereinigung im Beispiel Ausführen Sie zur Verfügung gestellt.
Persönlich versuche ich nicht zu Nest innerhalb der gleichen Methode ... mit der Ausnahme, ein Versuch / try / except / finally Szenario. Wenn ich mich zu verschachteln, um zu finden, dann zu mir, dass ist eine gute Zeit, Refactoring in einer anderen Methodenaufruf zu denken.
Bearbeiten ein bisschen dank der Kommentare Aufgeräumt von mghie und utku .
Bearbeiten geändert, um die Objekterstellung nicht Anwendung zu referenzieren, wie es ist nicht notwendig, in diesem Beispiel.
Andere Tipps
ich so etwas wie diese verwenden würde:
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;
Für die Implementierung der Schnittstelle finden Sie unter diese Artikel, aber man kann leicht etwas ähnliches selbst erstellen .
EDIT:
Ich habe gerade bemerkt, dass in dem verlinkten Artikel Schutz () ein Verfahren. In meinem eigenen Code ich habe Wächter () Funktionen überlastet, die TObject zurückkehren, oben geht davon aus Beispielcode etwas ähnliches. Natürlich mit Generika viel besser Code ist jetzt möglich ...
EDIT 2:
Wenn Sie sich fragen, warum versuchen ... schließlich vollständig in meinem Code entfernt wird: Es ist unmöglich, die verschachtelten Blöcke zu entfernen, ohne die Möglichkeit, Speicherlecks zu Einführung (wenn Destruktoren erhöhen Ausnahmen) oder Zugriffsverletzungen. Deshalb ist es am besten, eine Hilfsklasse zu verwenden, und lassen Sie die Referenzzählung von Schnittstellen vollständig übernehmen. Die Hilfsklasse können alle Objekte frei, es schützt, auch wenn einige der Destruktoren Ausnahmen erhöhen.
Es gibt eine weitere Variante des Codes ohne verschachtelte try ... schließlich, dass nur fiel mir ein. Wenn Sie nicht die Komponenten mit dem AOwner Parameter des auf Null gesetzt Konstruktor erstellen, dann kann man einfach nutzen die gesamte Lebensdauer-Management machen, dass die VCL Sie kostenlos gibt:
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;
Ich bin ein großer Anhänger des kleinen Code (wenn es nicht zu verschleiert wird).
Wenn Sie diesen (IMO) hässlich Weg zu gehen (Gruppe Umgang mit Initialisierung auf Null wissen, ob Befreiung erforderlich ist), müssen Sie mindestens garantieren, dass Sie nicht eine Ausnahme in eines des destructor verhindern lassen von befreien der Rest der Objekte.
So etwas wie:
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;
Es ist ein gutes Video auf Ausnahmen in Konstrukteuren & Destruktoren
Es zeigt einige schöne Beispiele wie:
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;
Was hat, wenn es in einem Fehler in dem destructor von CDS2
Cds1 nicht zerstört
werdenEDIT
Eine weitere gute Ressource ist:
Jim McKeeth exzellentes Video auf Delayed Ausnahmebehandlung in Codebereich III wurde er über Probleme spricht in Ausnahmen im finally-Block Handhabung.
@mghie: Delphi hat bekommt Stapel reserviert Objekte:
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;
Leider, wie das obige Beispiel zeigt:. Stapel zugeordneten Objekte nicht verhindern Speicherlecks
So würde dies immer noch einen Ruf an den destructor wie dies erforderlich:
var
MyObject: TMyObject;
begin
MyObject.Init;
try
/// ...
finally
MyObject.Done;
end;
end;
OK, ich gebe es zu, das ist fast off topic, aber ich dachte, es ist in diesem Zusammenhang interessant sein könnte, da Stapel zugeordneten Objekte als Lösung genannt wurden (was sie nicht sind, wenn es keine automatische Destruktoraufrufs ist).