تمرين - التقاط أنواع استثناء محددة
في وقت سابق من هذه الوحدة النمطية، تعلمت أن كائنات الاستثناء التي تم التقاطها بواسطة تطبيق C# الخاص بك هي مثيلات لفئة استثناء. بشكل عام، ستكون catch التعليمات البرمجية الخاصة بك واحدة مما يلي:
- كائن استثناء هو مثيل للفئة
System.Exceptionالأساسية. - كائن استثناء هو مثيل لنوع استثناء يرث من الفئة الأساسية. على سبيل المثال، مثيل للفئة
InvalidCastException.
فحص خصائص الاستثناء
System.Exception هي الفئة الأساسية التي ترث منها كافة أنواع الاستثناءات المشتقة. يرث كل نوع استثناء من الفئة الأساسية من خلال تسلسل هرمي معين للفئة. على سبيل المثال، التسلسل الهرمي للفئة InvalidCastException هو كما يلي:
Object
Exception
SystemException
InvalidCastException
لا تضيف معظم فئات الاستثناء التي ترث من Exception أي وظائف إضافية؛ فهي ببساطة ترث من Exception. لذلك، يمكنك فحص خصائص الفئة من Exception فهم معظم الاستثناءات، وكيف يمكنك استخدام استثناء في التعليمات البرمجية الخاصة بك.
فيما يلي خصائص Exception الفئة:
-
البيانات:
Dataتحتفظ الخاصية ببيانات عشوائية في أزواج قيم المفاتيح. -
HelpLink:
HelpLinkيمكن استخدام الخاصية للاحتفاظ بعنوان URL (أو URN) إلى ملف تعليمات يوفر معلومات شاملة حول سبب الاستثناء. -
HResult:
HResultيمكن استخدام الخاصية للوصول إلى قيمة رقمية مرمزة تم تعيينها إلى استثناء معين. -
InnerException:
InnerExceptionيمكن استخدام الخاصية لإنشاء سلسلة من الاستثناءات والاحتفاظ بها أثناء معالجة الاستثناءات. -
الرسالة:
Messageتوفر الخاصية تفاصيل حول سبب الاستثناء. -
المصدر:
Sourceيمكن استخدام الخاصية للوصول إلى اسم التطبيق أو العنصر الذي يسبب الخطأ. -
StackTrace:
StackTraceتحتوي الخاصية على تتبع مكدس يمكن استخدامه لتحديد مكان حدوث خطأ. -
TargetSite:
TargetSiteيمكن استخدام الخاصية للحصول على الأسلوب الذي يطرح الاستثناء الحالي.
لا بأس إذا كنت تشعر بالإرهاق قليلا من خلال هذا الفحص لخصائص الاستثناء والفئات الأساسية والميراث. لا تقلق، فإن التقاط الاستثناءات في التعليمات البرمجية والوصول إلى خصائص الاستثناء أسهل من شرح كيفية عمل الاستثناءات وخصائص الاستثناء.
إشعار
في هذه الوحدة النمطية، ستركز على استخدام خاصية رسالة الاستثناء للإبلاغ عن الاستثناء في واجهة مستخدم التطبيق الخاص بك.
الوصول إلى خصائص كائن استثناء
الآن بعد أن فهمت كائنات الاستثناء وخصائصها، حان الوقت لبدء الترميز.
تحديث ملف Program.cs كما يلي:
try { Process1(); } catch { Console.WriteLine("An exception has occurred"); } Console.WriteLine("Exit program"); static void Process1() { try { WriteMessage(); } catch { Console.WriteLine("Exception caught in Process1"); } } static void WriteMessage() { double float1 = 3000.0; double float2 = 0.0; int number1 = 3000; int number2 = 0; Console.WriteLine(float1 / float2); Console.WriteLine(number1 / number2); }خذ دقيقة لمراجعة التعليمات البرمجية.
هذه هي نفس التعليمات البرمجية التي رأيتها في الوحدة السابقة (رمز الحل لنشاط التحدي). أنت تعرف أنه يتم طرح استثناء أثناء
WriteMessageالأسلوب . تعرف أيضا أن الاستثناء يتم اكتشافه فيProcess1الأسلوب . ستستخدم هذه التعليمة البرمجية لفحص كائنات الاستثناء وأنواع الاستثناءات المحددة.حدّث الأسلوب
Process1على النحو التالي:static void Process1() { try { WriteMessage(); } catch (Exception ex) { Console.WriteLine($"Exception caught in Process1: {ex.Message}"); } }خذ دقيقة لفحص تحديثاتك.
لاحظ أن عبارة المحدثة
catchالخاصة بك تلتقط مثيلا للفئةExceptionفي كائن يسمىex. لاحظ أيضا أن الأسلوب الخاص بكConsole.WriteLine()يستخدمexللوصول إلى خاصية الكائنMessageوعرض رسالة الخطأ إلى وحدة التحكم.على الرغم من أنه يمكن استخدام العبارة
catchبدون وسيطات، لا يوصى بهذا الأسلوب. إذا لم تحدد وسيطة، يتم اكتشاف جميع أنواع الاستثناءات ولا يمكنك الاختراق بينها.بشكل عام، يجب عليك فقط التقاط الاستثناءات التي تعرف التعليمات البرمجية الخاصة بك كيفية التعافي منها. لذلك،
catchيجب أن تحدد عبارة وسيطة كائن مشتقة منSystem.Exception. يجب أن يكون نوع الاستثناء محددا قدر الإمكان. يساعد هذا على تجنب التقاط الاستثناءات التي لا يتمكن معالج الاستثناء من حلها. ستقوم بتحديث التعليمات البرمجية الخاصة بك لالتقاط نوع استثناء معين لاحقا في هذا التمرين.في القائمة ملف، حدد حفظ.
تعيين نقطة توقف على سطر التعليمات البرمجية التالي:
Console.WriteLine($"Exception caught in Process1: {ex.Message}");في قائمة Run ، حدد Start Debugging
يجب أن يتوقف تنفيذ التعليمات البرمجية مؤقتا عند نقطة التوقف.
مرر مؤشر الماوس فوق
ex.لاحظ أن IntelliSense يعرض نفس خصائص الاستثناء التي فحصتها سابقا.
خذ دقيقة لفحص المعلومات التي تصف كائن
exالاستثناء .لاحظ أن الاستثناء هو
System.DivideByZeroExceptionنوع استثناء وأن الخاصيةMessageمعينة إلىAttempted to divide by zero..لاحظ أن الخاصية
StackTraceتبلغ عن الأسلوب ورقم السطر حيث حدث الخطأ، جنبا إلى جنب مع تسلسل استدعاءات الأسلوب (وأرقام الأسطر) التي أدت إلى الخطأ.في شريط أدوات تتبع الأخطاء، حدد متابعة.
خذ دقيقة لفحص إخراج وحدة التحكم.
لاحظ أن خاصية الاستثناء
Messageمضمنة في الإخراج الذي تم إنشاؤه بواسطة التطبيق الخاص بك:∞ Exception caught in Process1: Attempted to divide by zero. Exit program
التقاط نوع استثناء معين
الآن بعد أن عرفت نوع الاستثناء الذي يجب التقاطه، يمكنك تحديث catch العبارة للتعامل مع نوع الاستثناء المحدد هذا.
حدّث الأسلوب
Process1على النحو التالي:static void Process1() { try { WriteMessage(); } catch (DivideByZeroException ex) { Console.WriteLine($"Exception caught in Process1: {ex.Message}"); } }احفظ التعليمات البرمجية الخاصة بك، ثم ابدأ جلسة تصحيح الأخطاء.
لاحظ أن التطبيق المحدث الخاص بك يبلغ عن نفس الرسائل إلى وحدة التحكم.
على الرغم من أن الرسائل التي تم الإبلاغ عنها هي نفسها، إلا أن هناك اختلافا مهما. سيلتقط الأسلوب الخاص بك
Process1استثناءات من النوع المحدد الذي هو مستعد للتعامل معه فقط.لإنشاء نوع استثناء مختلف، قم بتحديث
WriteMessageالأسلوب كما يلي:static void WriteMessage() { double float1 = 3000.0; double float2 = 0.0; int number1 = 3000; int number2 = 0; byte smallNumber; Console.WriteLine(float1 / float2); // Console.WriteLine(number1 / number2); checked { smallNumber = (byte)number1; } }لاحظ استخدام العبارة
checked.عند إجراء عمليات حسابية للنوع المتكامل التي تعين قيمة نوع متكامل إلى نوع متكامل آخر، تعتمد النتيجة على سياق التحقق من التجاوز.
checkedفي سياق ما، ينجح التحويل إذا كانت قيمة المصدر ضمن نطاق نوع الوجهة. وإلا،OverflowExceptionيتم طرح. في سياق غير محدد، ينجح التحويل دائما، ويتابع على النحو التالي:إذا كان نوع المصدر أكبر من نوع الوجهة، اقتطاع قيمة المصدر عن طريق تجاهل البتات "الإضافية" الأكثر أهمية. ثم يتم التعامل مع النتيجة كقيمة لنوع الوجهة.
إذا كان نوع المصدر أصغر من نوع الوجهة، فإن قيمة المصدر إما تم توسيعها أو توسيعها صفريا بحيث تكون بنفس حجم نوع الوجهة. يتم استخدام ملحق التوقيع إذا تم توقيع نوع المصدر؛ يتم استخدام ملحق صفري إذا كان نوع المصدر غير موقع. ثم يتم التعامل مع النتيجة كقيمة لنوع الوجهة.
إذا كان نوع المصدر هو نفس حجم نوع الوجهة، التعامل مع قيمة المصدر كقيمة لنوع الوجهة.
إشعار
يتم التعامل مع حسابات النوع المتكاملة غير الموجودة داخل
checkedكتلة التعليمات البرمجية كما لو كانت داخل كتلة التعليمات البرمجيةunchecked.احفظ التعليمات البرمجية الخاصة بك، ثم ابدأ جلسة تصحيح الأخطاء.
لاحظ أن نوع استثناء جديد يتم التقاطه بواسطة
catchعبارة في عبارات المستوى الأعلى بدلا من داخلProcess1الأسلوب.يقوم التطبيق الخاص بك بطباعة الرسائل التالية إلى وحدة التحكم:
∞ An exception has occurred Exit programإشعار
catchلم يتم تنفيذ الكتلة فيProcess1. هذا هو السلوك الذي أردته. التقاط الاستثناءات التي تكون التعليمات البرمجية الخاصة بك جاهزة للتعامل معها فقط.
التقاط استثناءات متعددة في كتلة التعليمات البرمجية
في هذه المرحلة، قد تتساءل عما يحدث عند حدوث استثناءات متعددة في كتلة تعليمة برمجية واحدة. هل ستسفر التعليمات البرمجية catch الخاصة بك عن كل استثناء عند حدوثها؟
حدّث الأسلوب
WriteMessageعلى النحو التالي:static void WriteMessage() { double float1 = 3000.0; double float2 = 0.0; int number1 = 3000; int number2 = 0; byte smallNumber; Console.WriteLine(float1 / float2); Console.WriteLine(number1 / number2); checked { smallNumber = (byte)number1; } }تعيين نقطة توقف داخل
WriteMessage()الأسلوب على سطر التعليمات البرمجية التالي:Console.WriteLine(float1 / float2);احفظ التعليمات البرمجية الخاصة بك، ثم ابدأ جلسة تصحيح الأخطاء.
تنقل عبر التعليمات البرمجية سطرا واحدا في كل مرة، ولاحظ ما يحدث بعد أن تعالج التعليمات البرمجية الاستثناء الأول.
عند حدوث الاستثناء الأول، يتم تمرير عنصر التحكم إلى العبارة الأولى
catchالتي يمكنها معالجة الاستثناء. لا يتم الوصول إلى التعليمات البرمجية التي من شأنها إنشاء الاستثناء الثاني أبدا. وهذا يعني أن بعض التعليمات البرمجية الخاصة بك لا يتم تنفيذها أبدا. ويمكن أن يؤدي ذلك إلى مشاكل خطيرة.خذ دقيقة للنظر في كيفية إدارة استثناءات متعددة، ومتى/لماذا قد لا تريد أن تقوم التعليمات البرمجية بإدارة استثناءات متعددة.
لقد تعلمت سابقا في هذه الوحدة النمطية أنه يجب اكتشاف الاستثناءات بالقرب من مكان حدوثها قدر الإمكان. مع مراعاة ذلك، يمكنك اختيار تحديث
WriteMessageالأسلوب لالتقاط الاستثناءات باستخدام الخاص بهtry-catch. على سبيل المثال:static void WriteMessage() { double float1 = 3000.0; double float2 = 0.0; int number1 = 3000; int number2 = 0; byte smallNumber; try { Console.WriteLine(float1 / float2); Console.WriteLine(number1 / number2); } catch (DivideByZeroException ex) { Console.WriteLine($"Exception caught in WriteMessage: {ex.Message}"); } checked { smallNumber = (byte)number1; } }يمكنك أيضا التفاف التعليمات
OverflowExceptionالبرمجية التي تتسبب في في منفصلtry-catchداخلWriteMessage()الأسلوب .checked { try { smallNumber = (byte)number1; } catch (OverflowException ex) { Console.WriteLine($"Exception caught in WriteMessage: {ex.Message}"); } }وفي أي ظروف سيكون من غير المرغوب فيه اصطياد الاستثناءات اللاحقة؟
ضع في اعتبارك الحالة عندما يقوم الأسلوب (أو كتلة التعليمات البرمجية) بإكمال عملية جزئين. افترض أن الجزء الثاني من العملية يعتمد على إكمال الجزء الأول. إذا تعذر إكمال الجزء الأول من العملية بنجاح، فلا جدوى من المتابعة إلى الجزء الثاني من العملية. في هذه الحالة، غالبا ما يكون من الأفضل تقديم رسالة للمستخدم توضح حالة الخطأ دون محاولة الجزء أو الأجزاء المتبقية من العملية الأكبر.
التقاط أنواع استثناء منفصلة في كتلة التعليمات البرمجية
هناك أوقات قد تتسبب فيها الاختلافات في بياناتك في أنواع مختلفة من الاستثناءات.
امسح نقاط التوقف، ثم استبدل محتويات ملف Program.cs بالتعليمات البرمجية التالية:
// inputValues is used to store numeric values entered by a user string[] inputValues = new string[]{"three", "9999999999", "0", "2" }; foreach (string inputValue in inputValues) { int numValue = 0; try { numValue = int.Parse(inputValue); } catch (FormatException) { Console.WriteLine("Invalid readResult. Please enter a valid number."); } catch (OverflowException) { Console.WriteLine("The number you entered is too large or too small."); } catch(Exception ex) { Console.WriteLine(ex.Message); } }خذ دقيقة لمراجعة هذه التعليمات البرمجية.
أولا، تنشئ التعليمات البرمجية صفيف سلسلة يسمى
inputValues. تهدف البيانات الموجودة في الصفيف إلى تمثيل قيم الإدخال التي أدخلها مستخدم طلب منه إدخال قيم رقمية. اعتمادا على القيمة التي تم إدخالها، قد تحدث أنواع استثناء مختلفة.لاحظ أن التعليمات البرمجية
int.Parseتستخدم الأسلوب لتحويل قيم "إدخال" السلسلة إلى أعداد صحيحة.int.Parseيتم وضع التعليمات البرمجية داخل كتلة التعليمات البرمجيةtry.تعيين نقطة توقف على سطر التعليمات البرمجية التالي:
int numValue = 0;احفظ التعليمات البرمجية الخاصة بك، ثم ابدأ جلسة تصحيح الأخطاء.
تنقل عبر التعليمات البرمجية سطرا واحدا في كل مرة، ولاحظ أنه يتم التقاط أنواع استثناء مختلفة.
خلاصة
فيما يلي بعض الأشياء المهمة التي يجب تذكرها من هذه الوحدة:
-
catchيجب تكوين العبارة لالتقاط نوع استثناء معين. على سبيل المثال، نوع الاستثناءDivideByZeroException. - يمكن الوصول إلى خصائص كائن استثناء داخل
catchالكتلة. على سبيل المثال، يمكنك استخدام الخاصيةMessageلإعلام مستخدم التطبيق بوجود مشكلة. - يمكنك تحديد بندين أو أكثر
catchعندما تحتاج إلى التقاط أكثر من نوع استثناء واحد.