أفضل الممارسات للقيام بمحاولة متداخلة / بيان أخيرا
-
29-08-2019 - |
سؤال
مرحبا، ما هي أفضل طريقة للقيام بمحاولة متداخلة وأخيرا البيانات في دلفي؟
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;
لتنفيذ الواجهة هذه مقال، ولكن يمكنك بسهولة إنشاء شيء مماثل لنفسك.
تعديل:
لقد لاحظت أن ذلك في حارس المقال المرتبط () إجراء. في التعليمات البرمجية الخاصة بي، لدي وظائف الحرس المثقلة () التي عودة TOBJECT، فوق نموذج التعليمة البرمجية يفترض شيئا مشابها. بالطبع مع Generic Code أفضل بكثير ممكن الآن ...
تحرير 2:
إذا كنت تتساءل لماذا حاولت ... أخيرا إزالتها بالكامل في التعليمات البرمجية: من المستحيل إزالة الكتل المتداخلة دون تقديم إمكانية تسريب الذاكرة (عند رفع المدمرين استثناءات) أو انتهاكات الوصول. لذلك من الأفضل استخدام فئة المساعد، واسمحوا مرجع المرجعية للواجهات تولي بالكامل. يمكن للفئة المساعد تحرير جميع الكائنات التي يحرسها، حتى لو رفع بعض المدمرين استثناءات.
هناك تباين آخر للرمز دون محاولة متداخلة ... وأخيرا حدث فقط لي. إذا لم تقم بإنشاء المكونات مع المعلمة Aowner للمشكلات التي تم تعيينها إلى NIL، فيمكنك ببساطة الاستفادة من إدارة العمر الذي يمنحك 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;
أنا مؤمن كبير في الرمز الصغير (إذا لم يكن مضايقا جدا).
إذا كنت ترغب في الذهاب إلى هذا الطريق القبيح (المنظمة البحرية الدولية) (التعامل مع المجموعة مع التهيئة إلى 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
تعديل
مورد جيد آخر هو:
جيم ماكيث فيديو ممتاز تأخير التعامل مع الاستثناء في نطاق التعليمات البرمجية، كان يتحدث عن مشاكل في التعامل مع الاستثناءات في الكتلة في النهاية.
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;
حسنا، أنا أعترف بذلك، هذا خارج الموضوع تقريبا، لكنني اعتقدت أنه قد يكون مثيرا للاهتمام في هذا السياق نظرا لأن الكائنات المخصصة للمكدسة تم ذكرها كحل (لا توجد مكالمة متداخلة أو تلقائية).