แบบฝึกหัด - ตรวจจับชนิดข้อยกเว้นเฉพาะ

เสร็จสมบูรณ์เมื่อ

ก่อนหน้านี้ในโมดูลนี้ คุณได้เรียนรู้ว่าออบเจ็กต์ข้อยกเว้นที่แอปพลิเคชัน 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. บนเมนู เรียกใช้ เลือก เริ่มการดีบัก

    การดําเนินการโค้ดควรหยุดชั่วคราวที่จุดสั่งหยุด

  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 จะถูกโยน ในบริบทที่ไม่ได้เลือก การแปลงจะประสบความสําเร็จเสมอและดําเนินการดังต่อไปนี้:

    • หากชนิดต้นทางมีขนาดใหญ่กว่าชนิดปลายทาง จากนั้นค่าต้นทางจะถูกตัดทอนโดยการละทิ้งบิตที่มีนัยสําคัญที่สุด "เพิ่มเติม" จากนั้นผลลัพธ์จะถือว่าเป็นค่าของชนิดปลายทาง

    • ถ้าชนิดต้นทางน้อยกว่าชนิดปลายทาง ค่าต้นทางจะเป็นการลงชื่อขยายหรือขยายศูนย์เพื่อให้มีขนาดเท่ากับชนิดปลายทาง ส่วนขยายป้ายจะถูกใช้ถ้ามีการลงนามชนิดแหล่งที่มา มีการใช้ zero-extension ถ้าชนิดแหล่งข้อมูลไม่มีเครื่องหมาย จากนั้นผลลัพธ์จะถือว่าเป็นค่าของชนิดปลายทาง

    • หากชนิดต้นทางมีขนาดเท่ากับชนิดปลายทาง ค่าต้นทางจะถือว่าเป็นค่าของชนิดปลายทาง

    หมายเหตุ

    การคํานวณชนิดจํานวนเต็มที่ไม่ได้อยู่ภายใน 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 เมื่อคุณจําเป็นต้องจับประเภทข้อยกเว้นมากกว่าหนึ่งชนิด