แบบฝึกหัด - ตรวจจับชนิดข้อยกเว้นเฉพาะ
ก่อนหน้านี้ในโมดูลนี้ คุณได้เรียนรู้ว่าออบเจ็กต์ข้อยกเว้นที่แอปพลิเคชัน 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}");บนเมนู เรียกใช้ เลือก เริ่มการดีบัก
การดําเนินการโค้ดควรหยุดชั่วคราวที่จุดสั่งหยุด
เลื่อนเคอร์เซอร์เมาส์ไปไว้เหนือ
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จะถูกโยน ในบริบทที่ไม่ได้เลือก การแปลงจะประสบความสําเร็จเสมอและดําเนินการดังต่อไปนี้:หากชนิดต้นทางมีขนาดใหญ่กว่าชนิดปลายทาง จากนั้นค่าต้นทางจะถูกตัดทอนโดยการละทิ้งบิตที่มีนัยสําคัญที่สุด "เพิ่มเติม" จากนั้นผลลัพธ์จะถือว่าเป็นค่าของชนิดปลายทาง
ถ้าชนิดต้นทางน้อยกว่าชนิดปลายทาง ค่าต้นทางจะเป็นการลงชื่อขยายหรือขยายศูนย์เพื่อให้มีขนาดเท่ากับชนิดปลายทาง ส่วนขยายป้ายจะถูกใช้ถ้ามีการลงนามชนิดแหล่งที่มา มีการใช้ zero-extension ถ้าชนิดแหล่งข้อมูลไม่มีเครื่องหมาย จากนั้นผลลัพธ์จะถือว่าเป็นค่าของชนิดปลายทาง
หากชนิดต้นทางมีขนาดเท่ากับชนิดปลายทาง ค่าต้นทางจะถือว่าเป็นค่าของชนิดปลายทาง
หมายเหตุ
การคํานวณชนิดจํานวนเต็มที่ไม่ได้อยู่ภายใน
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เมื่อคุณจําเป็นต้องจับประเภทข้อยกเว้นมากกว่าหนึ่งชนิด