بعد قراءة "كيفية فصل رمز الواجهة والرمز الوظيفي (استنادًا إلى دلفي/VCL)"، ذكر أحد الأشخاص سؤالاً، وهو كيفية التعامل مع الأخطاء في الفئات من جانب الخادم.
في الهياكل القائمة على الوظيفة، نستخدم بشكل عام قيم إرجاع الوظيفة للإشارة إلى ما إذا تم تنفيذ الوظيفة بنجاح ولتقديم معلومات مثل أنواع الأخطاء. لذلك سيكون هناك كود على الشكل التالي:
RetVal := SomeFunctionToOpenFile();
إذا RetVal = E_SUCCESSED ثم
...
وإلا إذا كان RetVal = E_FILENOTFOUND إذن
...
وإلا إذا كان RetVal = E_FILEFORMATERR إذن
...
آخر بعد ذلك
...
من الشائع جدًا استخدام طريقة تقوم بإرجاع رمز خطأ، ولكن هناك مشكلتان في استخدام مثل هذه الطريقة:
1. يقوم بإنشاء بنية فرعية طويلة ومعقدة (عدد كبير من عبارات if أو case)، مما يجعل عملية التحكم معقدة.
2. قد تكون هناك أخطاء لم تتم معالجتها (إذا لم يحدد مستدعي الوظيفة قيمة الإرجاع)
الاستثناءات هي حل موجه للكائنات لمعالجة الأخطاء. يمكنه الإبلاغ عن الأخطاء، ولكن ما تحتاج إلى معرفته هو أن الاستثناء لم يتم رفعه بسبب الخطأ، ولكن ببساطة بسبب استخدام الرفع.
في Object Pascal، يتم استخدام الكلمة المحجوزة المرفوعة لطرح الاستثناءات. في أي وقت (حتى لو لم يحدث أي خطأ)، سيؤدي الرفع إلى حدوث استثناء.
يمكن أن تتسبب الاستثناءات في عودة التعليمات البرمجية فورًا من النقطة التي حدث فيها الاستثناء، وبالتالي حماية التعليمات البرمجية الحساسة أدناه من التنفيذ. لا يوجد فرق بين العودة من دالة من خلال استثناء والعودة من دالة بشكل طبيعي (التنفيذ حتى نهاية الدالة أو تنفيذ الخروج) للوظيفة نفسها التي تطرح الاستثناء. الفرق هو أنه في نهاية المتصل، بعد العودة من خلال استثناء، سيتم التقاط حقوق التنفيذ من خلال محاولة المتصل... باستثناء الكتل (إذا كانت موجودة). إذا لم تكن هناك محاولة... باستثناء الحظر عند المتصل، فلن يستمر تنفيذ العبارات اللاحقة، ولكنها ستعود إلى المتصل ذي المستوى الأعلى حتى يتم العثور على محاولة... باستثناء الكتلة التي يمكنها التعامل مع الاستثناء. بعد معالجة الاستثناء، سيستمر تنفيذ العبارات بعد كتلة المحاولة...باستثناء، ويتم ترك التحكم في الطبقة التي تعالج الاستثناء. عندما يشعر معالج الاستثناء أن معالجة الاستثناء ليست كاملة بما فيه الكفاية، فإنه يحتاج إلى المتصل ذي المستوى الأعلى لمواصلة المعالجة، ويمكنه إعادة طرح الاستثناء (باستخدام رفع بسيط؛) ونقل التحكم إلى المتصل ذو المستوى الأعلى .
إذا لم تكن هناك محاولة... باستثناء كتلة محددة مسبقًا على الإطلاق، فسيتم اكتشاف الاستثناء الأخير بواسطة المحاولة الخارجية... باستثناء كتلة VCL التي تغلف البرنامج بأكمله.
لذلك، لن تكون هناك استثناءات غير معالجة، بمعنى آخر، لن تكون هناك أخطاء غير معالجة (على الرغم من عدم المساواة بين الأخطاء والاستثناءات). وهذه أيضًا ميزة آلية الاستثناء على استخدام الأساليب التي تُرجع رموز الخطأ. بالإضافة إلى ذلك، بعد طرح الاستثناء، يكون اتجاه عملية التحكم واضحًا جدًا ولن يتسبب في فقدان العملية للسيطرة.
لإعطاء مثال عن كيفية عمل الاستثناءات، لنفترض أننا نريد فتح ملف بتنسيق معين:
حدد أولاً فئتي استثناء (موروثة من الاستثناء)
EFileNotFound = class(Exception);
EFileFormatErr = class(Exception);
لنفترض أن هناك زرًا في Form1، والضغط على الزر يفتح الملف:
الإجراء TForm1.Button1Click(Sender: TObject);
يبدأ
يحاول
ToOpenFile();
يستثني
على EFileNotFound القيام به
ShowMessage("عذرًا، لا يمكنني العثور على الملف");
onEFileFormatErr يفعل
ShowMessage('عذرًا، الملف ليس هو الملف الذي أريده');
على E: استثناء القيام به
ShowMessage(E.Message);
نهاية؛
نهاية؛
ووظيفة فتح الملف:
الإجراء ToOpenFile؛
varRetVal:Integer;
يبدأ
// بعض التعليمات البرمجية لفتح الملف
RetVal := -1; //فشل الفتح
إذا كان RetVal = 0 ثم //success
مخرج
وإلا إذا كان RetVal = -1 إذن
رفع EFileNotFound.Create("الملف غير موجود")
وإلا إذا كان RetVal = -2 إذن
رفع EFileFormatErr.Create('خطأ في تنسيق الملف')
آخر // خطأ آخر
رفع الاستثناء.إنشاء('خطأ غير معروف');
نهاية؛
في البرنامج، يستدعي TForm1.Button1Click ToOpenFile ويحاول الإعدادات المسبقة... باستثناء معالجة الاستثناءات التي قد يتم طرحها بواسطة ToOpenFile. بالطبع، يمكن أيضًا تبسيط كود معالجة الاستثناءات الخاص بـ TForm1.Button1Click:
الإجراء TForm1.Button1Click(Sender: TObject);
يبدأ
يحاول
ToOpenFile();
يستثني
ShowMessage("فشل فتح الملف");
نهاية؛
نهاية؛
يؤدي استخدام الاستثناءات إلى حل مشكلات استخدام الأساليب التي تُرجع رموز الخطأ. وبطبيعة الحال، فإن استخدام الاستثناءات لا يخلو من التكلفة. ستؤدي الاستثناءات إلى زيادة عبء البرنامج، لذا لا ينصح بإساءة استخدام الاستثناءات. هناك فرق كبير بين كتابة عدد قليل من المحاولات...إلا وكتابة آلاف المحاولات...إلا. على حد تعبير تشارلي كالفرتس: "يجب عليك استخدام المحاولة... باستثناء الكتل عندما يبدو ذلك مفيدًا. ولكن حاول ألا تكون متحمسًا جدًا لهذه التقنية."
بالإضافة إلى ذلك، يقدم Object Pascal تجربة فريدة من نوعها...أخيرًا بنية. قلت من قبل أنه لا يوجد فرق بين العودة من دالة عبر استثناء والعودة من دالة بشكل طبيعي. لذلك، سيتم تحرير الكائنات المحلية الموجودة في المكدس في الوظيفة تلقائيًا، ولكن لن يتم تحرير الكائنات الموجودة في الكومة. ومع ذلك، يعتمد نموذج كائن Object Pascal على المراجع الموجودة في الكومة، وليس على المكدس. لذلك، نحتاج أحيانًا إلى تنظيف بعض موارد الكائنات المحلية قبل العودة من دالة عبر استثناء. حاول...أخيرًا يحل هذه المشكلة.
لقد قمت بإعادة كتابة كود ToOpenFile أعلاه، هذه المرة باستخدام بعض الموارد أثناء عملية ToOpenFile، وتحرير هذه الموارد بعد حدوث الاستثناء (أو عدم حدوثه) قبل العودة من الوظيفة:
الإجراء ToOpenFile؛
varRetVal: عدد صحيح؛
الدفق: TStream؛
يبدأ
// بعض التعليمات البرمجية لفتح الملف
الدفق := TStream.Create;
RetVal := -1; //فشل الفتح
يحاول
إذا كان RetVal = 0 ثم //success
مخرج
وإلا إذا كان RetVal = -1 إذن
رفع EFileNotFound.Create("الملف غير موجود")
وإلا إذا كان RetVal = -2 إذن
رفع EFileFormatErr.Create('خطأ في تنسيق الملف')
آخر // خطأ آخر
رفع الاستثناء.إنشاء('خطأ غير معروف');
أخيراً
Stream.Free;
نهاية؛
نهاية؛
من خلال التنقل خلال الكود أعلاه، يمكننا أن نرى أنه حتى عندما تكون قيمة RetVal هي 0، بعد تنفيذ Exit، سيتم تنفيذ الكود في النهاية ثم العودة من الوظيفة. وهذا يضمن الإصدار الصحيح للموارد المحلية.
تختلف أغراض وسيناريوهات استخدام المحاولة...إلا والمحاولة...أخيرًا، ويخلط بينها العديد من المبتدئين. فيما يلي بعض المعلومات الشخصية للمؤلف: حاول...إلا يتم استخدامه بشكل عام من قبل المتصل لالتقاط الاستثناءات التي تطرحها الوظائف المستدعىة والتعامل معها. ومحاولة...أخيرًا تُستخدم بشكل عام للوظيفة التي تطرح الاستثناء لتنفيذ بعض أعمال تنظيف الموارد.
توفر البرمجة الموجهة للكائنات حلاً لمعالجة الأخطاء يسمى "الاستثناء". إذا تم استخدامه بحكمة، فإنه سيفيد عملنا ويمكنه تحسين جودة التعليمات البرمجية التي نكتبها بشكل كبير.
مايكروسوفت ([email protected]) 2001.7.25
المصدر الأصلي: وثيقة Sunistudio (http://www.sunistudio.com/asp/sunidoc.asp)