Domanda

Ciao. Qual è il modo migliore per eseguire istruzioni try & final nidificate in 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;

Potete suggerire un modo migliore per farlo?

È stato utile?

Soluzione

come circa il seguente:

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;

Questo mantiene compatta, e cerca solo di liberare le istanze che sono stati creati. Non c'è davvero alcun bisogno di eseguire la nidificazione dal momento che ogni fallimento si tradurrà nel cadere a fine e l'esecuzione di tutte le pulizia nell'esempio che hai fornito.

Personalmente cerco di non nido all'interno lo stesso metodo ... con l'eccezione che è una prova / prova / eccetto / finalmente scenario. Se mi trovo a dover nido, quindi per me questo è un grande momento di pensare il refactoring in un'altra chiamata al metodo.

Modifica ripulito un po 'grazie alle osservazioni mghie e utku .

modifica cambiato la creazione dell'oggetto di non riferimento all'applicazione, come non necessario in questo esempio.

Altri suggerimenti

mi piacerebbe usare qualcosa di simile:

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;

Per l'implementazione dell'interfaccia vedi questo articolo , ma si può facilmente creare qualcosa di te stesso simile .

Modifica

Ho appena notato che nella Guardia articolo collegato () è una procedura. Nel mio codice che ho sovraccaricato le funzioni di Guardia () che restituiscono TObject, sopra il codice di esempio presuppone qualcosa di simile. Naturalmente con i generici codice molto meglio oggi è possibile ...

EDIT 2:

Se vi chiedete perché try ... finally viene completamente rimosso nel mio codice: E 'impossibile rimuovere i blocchi annidati senza introdurre la possibilità di perdite di memoria (quando distruttori sollevano eccezioni) o violazioni di accesso. Quindi è meglio utilizzare una classe di supporto, e lasciare che il conteggio dei riferimenti di interfacce prendere in consegna completamente. La classe di supporto in grado di liberare tutti gli oggetti custodisce, anche se alcuni dei distruttori sollevano eccezioni.

C'è un'altra variante del codice senza provare nidificato ... infine, che appena venuto in mente. Se non si creano i componenti con il parametro AOwner del costruttore set a zero, allora si può semplicemente utilizzare la gestione della durata che la VCL ti dà gratuitamente:

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;

Sono un grande credente in piccolo codice (se non è troppo offuscato).

Se si vuole seguire questa (IMO) percorso brutto (gruppo trattamento effettuato con l'inizializzazione a zero per sapere se è necessaria liberazione), è almeno deve garantire che non si lascia un'eccezione in una delle distruttore impedisce di liberare il resto dei tuoi oggetti.
Qualcosa di simile:

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;

C'è un bel video su eccezioni nei costruttori e nei distruttori

Mostra alcuni esempi carini come:

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;

Cosa succede se si verifica un errore nel distruttore di cds2

Cds1 non verrà distrutto

MODIFICARE

Un'altra buona risorsa è:

Eccellente video di Jim McKeeth Gestione delle eccezioni ritardata nell'intervallo di codici III dove parla di problemi nella gestione delle eccezioni nel blocco finale.

@mghie: Delphi ha oggetti pila assegnato:

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;

Purtroppo, come l'esempio precedente mostra:. Stack assegnato oggetti non impediscono perdite di memoria

Quindi questo sarebbe ancora bisogno di una chiamata al distruttore in questo modo:

var
  MyObject: TMyObject;

begin
  MyObject.Init;
  try
    /// ...
  finally
    MyObject.Done;
  end;
end;

OK, l'ammetto, questo è quasi fuori tema, ma ho pensato che potrebbe essere interessante in questo contesto in quanto oggetti di stack allocato sono stati menzionati come una soluzione (che non sono se non c'è chiamata automatica distruttore).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top