كما هو واضح من معنى هذا المصطلح فهو استثناء عن قاعدة معينة .
و هي دلالة على وقوع مشكلة ما أثناء تنفيذ البرنامج مما يتسبب في توقف البرنامج وعدم قدرته على متابعة التنفيذ.
- ما هو الـ Exception Handling :
هو قدرة المبرمج على حل مثل هذه المشاكل التي تحدث أثناء تنفيذ البرنامج, وحصر هذه الـ Exceptions التي يمكن وقوعها في البرنامج, بحيث يتم تفاديها لكي لا تعيق عمل البرنامج ولا تسبب توقفه عن التنفيذ, وذلك بواسطة ميكانيزم معين سنتعرف عليه بعد قليل.
أمثلة على بعض الـ Exceptions الشائعة:
- القسمة على صفر "division by zero".
- نتيجة عملية حسابية ضخمة "arithmetic overflow".
- إرسال قيم خاطئة للدوال أو غير مناسبة "invalid method parameters".
وغيرها الكثير والكثير .
ولكي يتسع لديك مفهوم الـ Exception دعنا نأخذ مثالا على ذلك:
فمثلا: لو قمنا بكتابة برنامج بسيط لقسمة عددين كالتالي, و كتب المستخدم في خانة المقسوم عليه القيمة صفر:

عندما نطلب النتيجة, ستظهر رسالة خطأ كما في الشكل التالي:

فالذي حدث الآن هو Exception , لأن عملية القسمة على صفر عملية غير مقبولة!, وكما هو واضح من رسالة الخطأ أعلاه فإن نوع الـ Exception هنا هو ,DivideByZeroException وبعد ظهور الرسالة سيتوقف البرنامج عن التنفيذ وطبعا توقف البرنامج بهذا الشكل أمر غير مستحسن.!
ولن تستطيع إجبار المستخدم على عدم كتابة الصفر في خانة المقسوم عليه!!!! ..
مثال آخر على الـException على نفس البرنامج السابق, ولنفرض أن المستخدم أدخل في أحد الخانتين نص وليس رقم فلن تتم العملية وستظهر رسالة الخطأ التالية ولن يتابع البرنامج تنفيذ عمله .

ونوع الــException الذي حدث الآن هنا هو FormatException والذي ينتج عادة نتيجة إدخال قيمة غير صالحة.
و لذلك أولا كل ما على المبرمج التفكير به هو توقع مثل هذه الأخطاء " Exceptions " التي قد تحدث أثناء تنفيذ برنامجه لكي يقوم بعمل handling لها, ولكن كيف؟
وهو بواسطة الـ try, catch, and finally blocks " " والتي محور درسنا اليوم.
try { }
catch { }
finally { }
و التي يشترط لها الترتيب السابق ....
|
نكتب بداخل هذا الــblock كل ما نتوقع حدوث الـ Exception فيه ..أي أمر تتوقع حدوث Exception فيه فقم بوضعه هنا ...
|
Try block { } |
|
الأوامر الموجودة في هذا الــ block يتم تنفيذها فقط في حالة حدوث ال Exception وهي تقوم بمعالجة الـ Exception التي تنتج أثناء تنفيذ البرنامج أو بمعنى إمساكها و السيطرة عليها حتى لا تعيق عمل البرنامج .. |
Catch block{ } |
|
الأوامر التي نضعها بداخل هذا block يتم تنفيذها في جميع الحالات سواء حدث Exception في البرنامج أو لو لم يحدث .. |
Finally block { } |
بالنسبة للـ catch block فإني أود توضيح نقطة وهي أنه يمكننا كتابتها بأحد طريقتين :
finally block هي اختيارية و لا يشترط كتابتها دائما ..
و لكي تتضح الأمور لديك أكثر فإننا سوف نطبق ذلك على مثال عملي " برنامج قسمة عددين ", و سنقوم بعمل handling لنوعين من أنواع الـ Exception التي قد تحدث في البرنامج والتي تعرضنا لها في بداية الدرس, وهي:
1. FormatException و 2. DivideByZeroException و قد تعرفنا على سبب حدوث كل منهما ...
باعتبار أنك قمت بتصميم Form مشابه للتالي:

عند حدث النقر على الـButton "Divide " الموجود أعلاه, سنكتب الأوامر التالية في حدث button1_Click:
private void button1_Click(object sender, System.EventArgs e)
{
try
{
int firstNum=Convert.ToInt16(this.textBox1.Text);
int secondNum=Convert.ToInt16(this.textBox2.Text);
int result=firstNum/secondNum;
this.label2.Text=result.ToString();
}
// process invalid number format
catch(FormatException)
{ MessageBox.Show("Invalid input value, input values must be integer","error.."); }
// division generates DivideByZeroException if the secondNum is zero
catch(DivideByZeroException ex2)
{ MessageBox.Show(ex2.Message.ToString(),"error.."); }
}
ومخرجات البرنامج المتوقعة ستكون مشابهة لما يلي:

و قبل أن أتحدث عن تفاصيل البرنامج, دعونا نلقي نظرة على المخرجات لهذا البرنامج:
1- فأول نافذة تمت فيها عملية القسمة بنجاح.
2- و النافذة التالية توضح نتيجة إدخال المستخدم للقيمة "hello " في ثاني textBox فهنا عندما ينقر المستخدم على Divide , فلن يستطيع البرنامج من تحويل هذه القيمة المدخلة إلى النوع integer بواسطة الدالة Convert.ToInt16, و معروف أن وظيفة هذه الدالة Convert.ToInt16() هو تحويل القيمة الموجودة بين القوسين إلى النوع integer , فإذا لم تستطع الدالة من التحويل كما في هذه الحالة فستنتج لنا (FormatException) و ستظهر رسالة الخطأ للمستخدم ولن تتم طبعا عملية القسمة.
3-والنافذة التالية توضح نتيجة إدخال المستخدم للقيمة "0" في ثاني textBox , وبما أن عملية القسمة على صفر عملية غير مقبولة إذن سينتج لنا (DivideByZeroException) وستظهر رسالة الخطأ الموضحة في الشكل.
و الآن لنناقش الأوامر المكتوبة في حدث النقر للزر " Divide", خطوة خطوة:
في البداية المستخدم يقوم بإدخال القيم في مربعي النص, ثم ينقر على " Divide ", ليتم تنفيذ أوامر حدث النقر كالتالي:
أولا قمنا بتعريف try block وكما قلنا بأنه سيحتوي على الجزء من البرنامج الذي يمكن أن ينتج منه "Exception" وهذا الجزء في برنامجنا هو عملية القسمة لذلك وضعناها داخل الـ try block .
بداخل هذا الـblock عرفنا متغيرين firstNum و secondNum من النوع integer والتي تستقبل القيم المدخلة من المستخدم, و نلاحظ استخدامنا للدالة Convert.ToInt16 لتحويل القيم إلى النوع ,integer بعدها سنقوم بعملية القسمة ..
int result=firstNum/secondNum;
وتخزين الناتج في المتغير result من النوع integer أيضا , ثم بعد ذلك إخراج النتيجة على label2 ...
بعد ذلك عرّفنا two catch blocks واستخدمنا أحدها مع الـ FormatException, والأخرى مع الــ DivideByZeroException ..
فإذا أدخل المستخدم قيمة غير صالحة مثل النص ولم تستطع الدالة Convert.ToInt16 من تحويله إلى integer , عندها ستقوم هذه الدالة بتوليد FormatException, بعدها سيقوم البرنامج بعمل مقارنة بين هذا الـException الناتج, وبين نوع الـException الموجود بين قوسي الـ catch, فعندما يرى أنها متساوية سيقوم بتنفيذ الأسطر التي بداخل هذا الـ catch block, كالتالي:
catch(FormatException)
{ MessageBox.Show("Invalid input value, input values must be integer","error.."); }
والتي ستقوم بإظهار رسالة الخطأ "Invalid input value, input values must be integer" للمستخدم.
وإذا أدخل القيمة صفر في الخانة الثانية, فلن تتم عملية القسمة, و ستنتج الـ DivideByZeroException والتي سنعمل لها handling بواسطة الـcatch block التالية :
catch(DivideByZeroException ex2)
{ MessageBox.Show(ex2.Message.ToString(),"error.."); }
وهنا نلاحظ فرق بسيط عن الـ catch السابقة لهذه, وهي أننا هنا قمنا بتعريف الكائنex2 من الـclass DivideByZeroException , بحيث أن رسالة الخطأ هنا لم نكتبها نحن بل استخدمنا الخاصية Message مع الكائن ex2 لإظهار الرسالة التي سوف يصدرها النظام عن هذا الخطأ.
طبعا إذا تم تنفيذ الأسطر الموجودة داخل try دون حدوث أي Exception فسيتم إخراج النتيجة فورا, وسيتجاهل البرنامج الـ two catch blocks .
و بذلك يستطيع المبرمج تفادي الأخطاء التي قد تحدث أثناء تنفيذ البرنامج...
وبذلك أيضاً يكون المبرمج مسيطر على أي نوع من الأخطاء من الممكن أن تظهر له أثناء برمجته سواء كانت Compile Time error حيث سيكتشفه الكومبايلر، أو كانت Run Time error حيث سيسيطر عليه بالـException handler.... يبقى له فقط تفادي الخطأ المنطقي في البرنامج -أي استخدام منطق غير صحيح في البرمجة- لكي يكتب برامج بلا أخطاء...وحتى الخطأ المنطقي Logical error يمكنك السيطرة عليه في بيئة الدوت نت وذلك باستخدام الـbreak point، تذكر أن أنواع الأخطاء في البرمجة ثلاثة Compile Time error, Run Time error, and Logical error وأنك بقراءة هذا السطر تكون سيطرت عليها جميعها في برنامجك
أخيراً, أتمنى أن أكون وُفّقت في عرض الفكرة