최고의 연습을 중첩하려고 마지막으로 문의
-
29-08-2019 - |
문제
하이 가장 좋은 방법은 무엇입을 중첩된 try&마지막으로 문 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;
제안할 수 있습니다 더 나은 방법을 이?
해결책
다음은 어떻습니까 :
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를 반환하는 Guard () 함수를 과부하 시켰으며, 위의 샘플 코드는 비슷한 것을 가정합니다. 물론 제네릭을 사용하면 훨씬 더 나은 코드가 가능합니다 ...
편집 2 :
내 코드에서 마침내 완전히 제거 된 이유가 궁금하다면 : 메모리 누출 가능성 (소멸자가 예외를 제외 할 때) 또는 액세스 위반 가능성을 도입하지 않고 중첩 블록을 제거하는 것은 불가능합니다. 따라서 도우미 클래스를 사용하고 인터페이스의 참조 계산을 완전히 인수하는 것이 가장 좋습니다. 도우미 클래스는 일부 소멸자 중 일부가 예외를 제기하더라도 모든 대상을 보호 할 수 있습니다.
중첩 시도가없는 코드의 또 다른 변형이 있습니다 ... 마침내 나에게 일어난 일이 일어났습니다. NIL로 설정된 생성자의 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) 못생긴 경로 (프리가 필요한지 알기 위해 NIL에 대한 초기화로 그룹 취급)를 원한다면, 적어도 파괴자 중 하나에서 예외가 나머지 부분을 해방시키는 것을 방지하지 않도록 보장해야합니다. 당신의 대상.
같은 것 :
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:델파이어 스택 할당 개체:
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;
OK,나는 그것을 승인한다,이것은 매우 거의 주제,하지만 나는 생각에서 흥미로운 일이 될 수 있습니다 이 상황 때문에 스택 할당 개체었으로 언급된 솔루션(는 그들은하지 않는 경우는 없이 자동 소멸자출).