Лучшая практика, чтобы сделать вложенный оператор TRY / FINALLY

20
задан menjaraz 15 March 2012 в 12:25
поделиться

6 ответов

как насчет следующего:

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;

Это сохраняет это компактным, и только пытается освободить экземпляры, которые были созданы. Действительно нет никакой потребности выполнить вложение, так как ЛЮБОЙ отказ приведет к припаданию наконец и выполнение всей очистки в примере, который Вы обеспечили.

Лично я пробую не к вложенному множеству в рамках того же метода... за исключением, являющимся сценарием попытки/попытки/кроме/наконец. Если мне нужно к вложенному множеству, то мне, который является прекрасным временем для размышления, осуществляя рефакторинг в другой вызов метода.

РЕДАКТИРОВАНИЕ Очищенный немного благодаря комментариям mghie и utku.

РЕДАКТИРОВАНИЕ изменило создание объекта на не эталонное приложение как не необходимый в этом примере.

30
ответ дан 29 November 2019 в 22:58
поделиться

Я использовал бы что-то вроде этого:

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;

Для реализации интерфейса видят этот статья, но можно легко создать что-то подобное сами.

РЕДАКТИРОВАНИЕ:

я просто заметил, что в связанной статье Guard () процедура. В моем собственном коде я перегрузил охрану () функции, что TObject возврата, выше примера кода принимает что-то подобное. Конечно, с дженериками намного лучший код теперь возможен...

РЕДАКТИРОВАНИЕ 2:

, Если Вы задаетесь вопросом, почему попытка... наконец полностью удалена в моем коде: невозможно удалить вложенные блоки, не представляя возможность утечек памяти (когда деструкторы повышают исключения), или нарушения прав доступа. Поэтому лучше использовать класс помощника и позволять подсчету ссылок интерфейсов вступить во владение полностью. Класс помощника может освободить все объекты, которые он охраняет, даже если некоторые деструкторы повышают исключения.

18
ответ дан 29 November 2019 в 22:58
поделиться

Существует другое изменение кода без вложенной попытки... наконец, которая просто произошла со мной. Если Вы не создаете компоненты с параметром AOwner набора конструктора к нолю, то можно просто использовать пожизненное управление, которое VCL дает Вам бесплатно:

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;

я - крупный сторонник маленького кода (если он не слишком запутывается).

4
ответ дан 29 November 2019 в 22:58
поделиться

Если Вы захотите пойти этим ужасным путем (IMO) (обработка группы с инициализацией к нолю, чтобы знать, необходимо ли освобождение), то, по крайней мере, НЕОБХОДИМО гарантировать, что Вы не позволите исключению в одном из деструктора препятствовать освободить остальную часть Ваших объектов.
Что-то как:

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;
3
ответ дан 29 November 2019 в 22:58
поделиться

Существует хорошее видео на исключения в конструкторах & деструкторы

Это показывает некоторые хорошие примеры, такие как:

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;

, Что имеет, если там по ошибке в деструкторе cds2

Cds1 не будет Уничтожен

РЕДАКТИРОВАНИЕ

, Другой хороший ресурс:

Jim McKeeth превосходное видео на , которым была Задержанная Обработка исключений в диапазоне кода III, он говорит о проблемах в обрабатывании исключений в наконец блок.

2
ответ дан 29 November 2019 в 22:58
поделиться

@mghie: Delphi имеет выделенные объекты стека:

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;

, К сожалению, как вышеупомянутые шоу в качестве примера: выделенные объекты Стека не предотвращают утечки памяти.

, Таким образом, это все еще потребовало бы вызова к деструктору как это:

var
  MyObject: TMyObject;

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

хорошо, я допускаю его, это очень почти вне темы, но я думал, что это могло бы быть интересно в этом контексте, так как стек выделил объекты, были упомянуты как решение (который они не то, если нет никакого автоматического вызова деструктора).

1
ответ дан 29 November 2019 в 22:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: