تمرين - التقاط أنواع استثناء محددة

مكتمل

في وقت سابق من هذه الوحدة النمطية، تعلمت أن كائنات الاستثناء التي تم التقاطها بواسطة تطبيق 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 يمكن استخدام الخاصية للحصول على الأسلوب الذي يطرح الاستثناء الحالي.

لا بأس إذا كنت تشعر بالإرهاق قليلا من خلال هذا الفحص لخصائص الاستثناء والفئات الأساسية والميراث. لا تقلق، فإن التقاط الاستثناءات في التعليمات البرمجية والوصول إلى خصائص الاستثناء أسهل من شرح كيفية عمل الاستثناءات وخصائص الاستثناء.

إشعار

في هذه الوحدة النمطية، ستركز على استخدام خاصية رسالة الاستثناء للإبلاغ عن الاستثناء في واجهة مستخدم التطبيق الخاص بك.

الوصول إلى خصائص كائن استثناء

الآن بعد أن فهمت كائنات الاستثناء وخصائصها، حان الوقت لبدء الترميز.

  1. تحديث ملف 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);
    }
    
  2. خذ دقيقة لمراجعة التعليمات البرمجية.

    هذه هي نفس التعليمات البرمجية التي رأيتها في الوحدة السابقة (رمز الحل لنشاط التحدي). أنت تعرف أنه يتم طرح استثناء أثناء WriteMessage الأسلوب . تعرف أيضا أن الاستثناء يتم اكتشافه في Process1 الأسلوب . ستستخدم هذه التعليمة البرمجية لفحص كائنات الاستثناء وأنواع الاستثناءات المحددة.

  3. حدّث الأسلوب Process1 على النحو التالي:

    static void Process1()
    {
        try
        {
            WriteMessage();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception caught in Process1: {ex.Message}");
        }
    }
    
  4. خذ دقيقة لفحص تحديثاتك.

    لاحظ أن عبارة المحدثة catch الخاصة بك تلتقط مثيلا للفئة Exception في كائن يسمى ex. لاحظ أيضا أن الأسلوب الخاص بك Console.WriteLine() يستخدم ex للوصول إلى خاصية الكائن Message وعرض رسالة الخطأ إلى وحدة التحكم.

    على الرغم من أنه يمكن استخدام العبارة catch بدون وسيطات، لا يوصى بهذا الأسلوب. إذا لم تحدد وسيطة، يتم اكتشاف جميع أنواع الاستثناءات ولا يمكنك الاختراق بينها.

    بشكل عام، يجب عليك فقط التقاط الاستثناءات التي تعرف التعليمات البرمجية الخاصة بك كيفية التعافي منها. لذلك، catch يجب أن تحدد عبارة وسيطة كائن مشتقة من System.Exception. يجب أن يكون نوع الاستثناء محددا قدر الإمكان. يساعد هذا على تجنب التقاط الاستثناءات التي لا يتمكن معالج الاستثناء من حلها. ستقوم بتحديث التعليمات البرمجية الخاصة بك لالتقاط نوع استثناء معين لاحقا في هذا التمرين.

  5. في القائمة ملف، حدد حفظ.

  6. تعيين نقطة توقف على سطر التعليمات البرمجية التالي:

    Console.WriteLine($"Exception caught in Process1: {ex.Message}");
    
  7. في قائمة Run ، حدد Start Debugging

    يجب أن يتوقف تنفيذ التعليمات البرمجية مؤقتا عند نقطة التوقف.

  8. مرر مؤشر الماوس فوق ex.

    لاحظ أن IntelliSense يعرض نفس خصائص الاستثناء التي فحصتها سابقا.

  9. خذ دقيقة لفحص المعلومات التي تصف كائن exالاستثناء .

    لاحظ أن الاستثناء هو System.DivideByZeroException نوع استثناء وأن الخاصية Message معينة إلى Attempted to divide by zero..

    لاحظ أن الخاصية StackTrace تبلغ عن الأسلوب ورقم السطر حيث حدث الخطأ، جنبا إلى جنب مع تسلسل استدعاءات الأسلوب (وأرقام الأسطر) التي أدت إلى الخطأ.

  10. في شريط أدوات تتبع الأخطاء، حدد متابعة.

  11. خذ دقيقة لفحص إخراج وحدة التحكم.

    لاحظ أن خاصية الاستثناء Message مضمنة في الإخراج الذي تم إنشاؤه بواسطة التطبيق الخاص بك:

    ∞
    Exception caught in Process1: Attempted to divide by zero.
    Exit program
    

التقاط نوع استثناء معين

الآن بعد أن عرفت نوع الاستثناء الذي يجب التقاطه، يمكنك تحديث catch العبارة للتعامل مع نوع الاستثناء المحدد هذا.

  1. حدّث الأسلوب Process1 على النحو التالي:

    static void Process1()
    {
        try
        {
            WriteMessage();
        }
        catch (DivideByZeroException ex)
        {
            Console.WriteLine($"Exception caught in Process1: {ex.Message}");
        }
    }
    
  2. احفظ التعليمات البرمجية الخاصة بك، ثم ابدأ جلسة تصحيح الأخطاء.

  3. لاحظ أن التطبيق المحدث الخاص بك يبلغ عن نفس الرسائل إلى وحدة التحكم.

    على الرغم من أن الرسائل التي تم الإبلاغ عنها هي نفسها، إلا أن هناك اختلافا مهما. سيلتقط الأسلوب الخاص بك Process1 استثناءات من النوع المحدد الذي هو مستعد للتعامل معه فقط.

  4. لإنشاء نوع استثناء مختلف، قم بتحديث 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;
        }
    }
    
  5. لاحظ استخدام العبارة checked .

    عند إجراء عمليات حسابية للنوع المتكامل التي تعين قيمة نوع متكامل إلى نوع متكامل آخر، تعتمد النتيجة على سياق التحقق من التجاوز. checked في سياق ما، ينجح التحويل إذا كانت قيمة المصدر ضمن نطاق نوع الوجهة. وإلا، OverflowException يتم طرح. في سياق غير محدد، ينجح التحويل دائما، ويتابع على النحو التالي:

    • إذا كان نوع المصدر أكبر من نوع الوجهة، اقتطاع قيمة المصدر عن طريق تجاهل البتات "الإضافية" الأكثر أهمية. ثم يتم التعامل مع النتيجة كقيمة لنوع الوجهة.

    • إذا كان نوع المصدر أصغر من نوع الوجهة، فإن قيمة المصدر إما تم توسيعها أو توسيعها صفريا بحيث تكون بنفس حجم نوع الوجهة. يتم استخدام ملحق التوقيع إذا تم توقيع نوع المصدر؛ يتم استخدام ملحق صفري إذا كان نوع المصدر غير موقع. ثم يتم التعامل مع النتيجة كقيمة لنوع الوجهة.

    • إذا كان نوع المصدر هو نفس حجم نوع الوجهة، التعامل مع قيمة المصدر كقيمة لنوع الوجهة.

    إشعار

    يتم التعامل مع حسابات النوع المتكاملة غير الموجودة داخل checked كتلة التعليمات البرمجية كما لو كانت داخل كتلة التعليمات البرمجية unchecked .

  6. احفظ التعليمات البرمجية الخاصة بك، ثم ابدأ جلسة تصحيح الأخطاء.

  7. لاحظ أن نوع استثناء جديد يتم التقاطه بواسطة catch عبارة في عبارات المستوى الأعلى بدلا من داخل Process1 الأسلوب.

    يقوم التطبيق الخاص بك بطباعة الرسائل التالية إلى وحدة التحكم:

    ∞
    An exception has occurred
    Exit program
    

    إشعار

    catch لم يتم تنفيذ الكتلة فيProcess1. هذا هو السلوك الذي أردته. التقاط الاستثناءات التي تكون التعليمات البرمجية الخاصة بك جاهزة للتعامل معها فقط.

التقاط استثناءات متعددة في كتلة التعليمات البرمجية

في هذه المرحلة، قد تتساءل عما يحدث عند حدوث استثناءات متعددة في كتلة تعليمة برمجية واحدة. هل ستسفر التعليمات البرمجية catch الخاصة بك عن كل استثناء عند حدوثها؟

  1. حدّث الأسلوب 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;
        }
    }
    
  2. تعيين نقطة توقف داخل WriteMessage() الأسلوب على سطر التعليمات البرمجية التالي:

    Console.WriteLine(float1 / float2);
    
  3. احفظ التعليمات البرمجية الخاصة بك، ثم ابدأ جلسة تصحيح الأخطاء.

  4. تنقل عبر التعليمات البرمجية سطرا واحدا في كل مرة، ولاحظ ما يحدث بعد أن تعالج التعليمات البرمجية الاستثناء الأول.

    عند حدوث الاستثناء الأول، يتم تمرير عنصر التحكم إلى العبارة الأولى catch التي يمكنها معالجة الاستثناء. لا يتم الوصول إلى التعليمات البرمجية التي من شأنها إنشاء الاستثناء الثاني أبدا. وهذا يعني أن بعض التعليمات البرمجية الخاصة بك لا يتم تنفيذها أبدا. ويمكن أن يؤدي ذلك إلى مشاكل خطيرة.

  5. خذ دقيقة للنظر في كيفية إدارة استثناءات متعددة، ومتى/لماذا قد لا تريد أن تقوم التعليمات البرمجية بإدارة استثناءات متعددة.

    لقد تعلمت سابقا في هذه الوحدة النمطية أنه يجب اكتشاف الاستثناءات بالقرب من مكان حدوثها قدر الإمكان. مع مراعاة ذلك، يمكنك اختيار تحديث 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}");
        }  
    }
    
  6. وفي أي ظروف سيكون من غير المرغوب فيه اصطياد الاستثناءات اللاحقة؟

    ضع في اعتبارك الحالة عندما يقوم الأسلوب (أو كتلة التعليمات البرمجية) بإكمال عملية جزئين. افترض أن الجزء الثاني من العملية يعتمد على إكمال الجزء الأول. إذا تعذر إكمال الجزء الأول من العملية بنجاح، فلا جدوى من المتابعة إلى الجزء الثاني من العملية. في هذه الحالة، غالبا ما يكون من الأفضل تقديم رسالة للمستخدم توضح حالة الخطأ دون محاولة الجزء أو الأجزاء المتبقية من العملية الأكبر.

التقاط أنواع استثناء منفصلة في كتلة التعليمات البرمجية

هناك أوقات قد تتسبب فيها الاختلافات في بياناتك في أنواع مختلفة من الاستثناءات.

  1. امسح نقاط التوقف، ثم استبدل محتويات ملف 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);
        }
    }
    
  2. خذ دقيقة لمراجعة هذه التعليمات البرمجية.

    أولا، تنشئ التعليمات البرمجية صفيف سلسلة يسمى inputValues. تهدف البيانات الموجودة في الصفيف إلى تمثيل قيم الإدخال التي أدخلها مستخدم طلب منه إدخال قيم رقمية. اعتمادا على القيمة التي تم إدخالها، قد تحدث أنواع استثناء مختلفة.

    لاحظ أن التعليمات البرمجية int.Parse تستخدم الأسلوب لتحويل قيم "إدخال" السلسلة إلى أعداد صحيحة. int.Parse يتم وضع التعليمات البرمجية داخل كتلة التعليمات البرمجيةtry.

  3. تعيين نقطة توقف على سطر التعليمات البرمجية التالي:

    int numValue = 0;
    
  4. احفظ التعليمات البرمجية الخاصة بك، ثم ابدأ جلسة تصحيح الأخطاء.

  5. تنقل عبر التعليمات البرمجية سطرا واحدا في كل مرة، ولاحظ أنه يتم التقاط أنواع استثناء مختلفة.

خلاصة

فيما يلي بعض الأشياء المهمة التي يجب تذكرها من هذه الوحدة:

  • catch يجب تكوين العبارة لالتقاط نوع استثناء معين. على سبيل المثال، نوع الاستثناء DivideByZeroException .
  • يمكن الوصول إلى خصائص كائن استثناء داخل catch الكتلة. على سبيل المثال، يمكنك استخدام الخاصية Message لإعلام مستخدم التطبيق بوجود مشكلة.
  • يمكنك تحديد بندين أو أكثر catch عندما تحتاج إلى التقاط أكثر من نوع استثناء واحد.