как насчет следующего:
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.
РЕДАКТИРОВАНИЕ изменило создание объекта на не эталонное приложение как не необходимый в этом примере.
Я использовал бы что-то вроде этого:
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:
, Если Вы задаетесь вопросом, почему попытка... наконец полностью удалена в моем коде: невозможно удалить вложенные блоки, не представляя возможность утечек памяти (когда деструкторы повышают исключения), или нарушения прав доступа. Поэтому лучше использовать класс помощника и позволять подсчету ссылок интерфейсов вступить во владение полностью. Класс помощника может освободить все объекты, которые он охраняет, даже если некоторые деструкторы повышают исключения.
Существует другое изменение кода без вложенной попытки... наконец, которая просто произошла со мной. Если Вы не создаете компоненты с параметром 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;
я - крупный сторонник маленького кода (если он не слишком запутывается).
Если Вы захотите пойти этим ужасным путем (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;
Существует хорошее видео на исключения в конструкторах & деструкторы
Это показывает некоторые хорошие примеры, такие как:
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, он говорит о проблемах в обрабатывании исключений в наконец блок.
@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;
хорошо, я допускаю его, это очень почти вне темы, но я думал, что это могло бы быть интересно в этом контексте, так как стек выделил объекты, были упомянуты как решение (который они не то, если нет никакого автоматического вызова деструктора).