Pregunta

Hola ¿Cuál es la mejor manera de hacerlo try anidado y finally en 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;

Puede sugerir una mejor manera de hacer esto?

¿Fue útil?

Solución

¿qué hay de lo siguiente:

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;

Esto lo mantiene compacto, y sólo intenta liberar los casos que fueron creados. En realidad no hay necesidad de llevar a cabo la anidación ya que cualquier fallo ocasionará una caída al fin y la realización de la totalidad de la limpieza en el ejemplo que nos ha facilitado.

En lo personal yo no trato de nido dentro del mismo método ... con la excepción de un try / try / excepto / escenario finalmente. Si me encuentro que necesitan para anidar, y luego a mí que es un buen momento para pensar en la refactorización en otra llamada al método.

Editar Se ha limpiado un poco gracias a los comentarios de mghie y utku .

editar cambió la creación del objeto de no referencia de la aplicación, ya que no es necesario en este ejemplo.

Otros consejos

Me gustaría utilizar algo como esto:

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;

Para la implementación de la interfaz ver este artículo , pero se pueden crear fácilmente algo por sí mismo semejante .

EDIT:

Acabo de notar que en la Guardia artículo enlazado () es un procedimiento. En mi propio código he sobrecargado funciones de Guardia () que devuelven TObject, por encima de código de ejemplo se supone algo similar. Por supuesto, con los genéricos mucho mejor código es ahora posible ...

EDIT 2:

Si usted se pregunta por qué tratar ... finalmente es eliminado por completo en mi código: Es imposible eliminar los bloques anidados sin introducir la posibilidad de fugas de memoria (cuando los destructores plantean excepciones) o violaciónes de acceso. Por lo tanto, lo mejor es utilizar una clase de ayuda, y dejar que el recuento de referencias de interfaces de hacerse cargo por completo. La clase de ayuda puede liberar a todos los objetos que guardias, incluso si algunos de los destructores lanzar excepciones.

Hay otra variación del código sin tratar anidado ... finalmente, que me acaba de ocurrir. Si no se crea los componentes con el parámetro AOwner del constructor set a cero, entonces usted puede simplemente hacer uso de la administración de la duración que el VCL le da de forma gratuita:

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;

Soy un gran creyente en pequeño código (si no es demasiado ofuscado).

Si desea seguir este (OMI) ruta fea (grupo con el manejo de inicialización a cero para saber si se necesita liberación), que al menos debe garantizar que no se deje una excepción en una de las destructor impedir la liberación el resto de los objetos.
Algo así como:

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;

No es un buen video en href="http://codegearguru.com/video/030/TSuicide.html" de constructores y destructores

Se muestra algunos ejemplos agradables tales como:

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;

¿Qué tiene que si hay un error en el destructor de cds2

Cds1 no será destruido

editar

Otro buen recurso es:

Jim McKeeth excelente video en retardada control de excepciones en un rango de códigos III se habla acerca de los problemas en el manejo de excepciones en el bloque finally.

@mghie: Delphi tiene objetos pila asignado:

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;

Por desgracia, como muestra el ejemplo anterior:. Objetos pila asignado no evitan pérdidas de memoria

Así que esto todavía requeriría una llamada al destructor de esta manera:

var
  MyObject: TMyObject;

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

OK, lo admito, esto es casi fuera de tema, pero pensé que podría ser interesante en este contexto, ya que los objetos de la pila asignado se mencionaron como una solución (que no son si no hay una llamada al destructor automática).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top