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?

War es hilfreich?

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

werden

EDIT

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).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top