หลังจากอ่าน "วิธีแยกโค้ดอินเทอร์เฟซและโค้ดการทำงาน (อิงจาก Delphi/VCL)" ของฉันแล้ว ก็มีคนพูดถึงคำถาม ซึ่งเป็นวิธีจัดการกับข้อผิดพลาดในคลาสฝั่งเซิร์ฟเวอร์
ในโครงสร้างตามฟังก์ชัน โดยทั่วไปเราใช้ค่าส่งคืนฟังก์ชันเพื่อระบุว่าฟังก์ชันได้รับการดำเนินการสำเร็จหรือไม่ และเพื่อให้ข้อมูล เช่น ประเภทข้อผิดพลาด โดยจะมีโค้ดอยู่ในรูปแบบดังนี้
RetVal := SomeFunctionToOpenFile();
ถ้า RetVal = E_SUCCESSED แล้ว
-
อย่างอื่นถ้า RetVal = E_FILENOTFOUND แล้ว
-
อย่างอื่นถ้า RetVal = E_FILEFORMATERR แล้ว
-
อย่างอื่นแล้ว
-
เป็นเรื่องปกติมากที่จะใช้วิธีการส่งคืนรหัสข้อผิดพลาด แต่การใช้วิธีนี้มีปัญหาสองประการ:
1. สร้างโครงสร้างสาขาที่ยาวและซับซ้อน (คำสั่ง if หรือ case จำนวนมาก) ทำให้กระบวนการควบคุมซับซ้อน
2. อาจมีข้อผิดพลาดที่ไม่ได้รับการจัดการ (หากผู้เรียกใช้ฟังก์ชันไม่ได้กำหนดค่าที่ส่งคืน)
ข้อยกเว้นเป็นโซลูชันเชิงวัตถุสำหรับการจัดการข้อผิดพลาด สามารถรายงานข้อผิดพลาดได้ แต่สิ่งที่คุณต้องรู้ก็คือ ข้อยกเว้นไม่ได้เกิดขึ้นเนื่องจากข้อผิดพลาด แต่เพียงเพราะมีการใช้การเพิ่ม
ใน Object Pascal คำสงวนที่ยกขึ้นจะใช้ในการส่งข้อยกเว้น เมื่อใดก็ได้ (แม้ว่าจะไม่มีข้อผิดพลาดเกิดขึ้น) การยกจะทำให้เกิดข้อยกเว้นเกิดขึ้น
ข้อยกเว้นอาจทำให้โค้ดส่งคืนทันทีจากจุดที่เกิดข้อยกเว้น ดังนั้นจึงป้องกันโค้ดที่ละเอียดอ่อนด้านล่างไม่ให้ถูกดำเนินการ ไม่มีความแตกต่างระหว่างการส่งคืนจากฟังก์ชันผ่านข้อยกเว้นและการส่งคืนจากฟังก์ชันตามปกติ (ดำเนินการไปยังจุดสิ้นสุดของฟังก์ชันหรือดำเนินการออก) สำหรับฟังก์ชันเองที่ส่งข้อยกเว้น ข้อแตกต่างก็คือที่จุดสิ้นสุดของผู้เรียก หลังจากส่งคืนผ่านข้อยกเว้น สิทธิ์ในการดำเนินการจะถูกยึดโดยความพยายามของผู้เรียก...ยกเว้นการบล็อก (ถ้ามี) หากไม่มี try...ยกเว้นบล็อกที่ผู้เรียก คำสั่งต่อมาจะไม่ถูกดำเนินการต่อไป แต่จะกลับไปยังผู้เรียกระดับที่สูงกว่าจนกว่าจะพบ try...ยกเว้นบล็อกที่สามารถจัดการข้อยกเว้นได้ หลังจากจัดการข้อยกเว้นแล้ว คำสั่งหลังจากบล็อก try...ยกเว้น จะยังคงถูกดำเนินการต่อไป และการควบคุมจะยังคงอยู่ในเลเยอร์ที่จัดการข้อยกเว้น เมื่อตัวจัดการข้อยกเว้นรู้สึกว่าการจัดการข้อยกเว้นยังไม่สมบูรณ์เพียงพอ ก็ต้องการให้ผู้เรียกระดับสูงกว่าดำเนินการประมวลผลต่อ โดยสามารถส่งข้อยกเว้นอีกครั้ง (ใช้การยกแบบธรรมดา) และถ่ายโอนการควบคุมไปยังผู้เรียกระดับที่สูงกว่า .
หากไม่มีความพยายาม...ยกเว้นบล็อกที่ตั้งไว้ล่วงหน้าเลย ข้อยกเว้นสุดท้ายจะถูกจับโดยความพยายามด้านนอกสุด...ยกเว้นบล็อกของ VCL ที่ห่อหุ้มโปรแกรมทั้งหมด
ดังนั้น จะไม่มีข้อยกเว้นที่ไม่สามารถจัดการได้ กล่าวคือ จะไม่มีข้อผิดพลาดที่ไม่สามารถจัดการได้ (แม้ว่าข้อผิดพลาดและข้อยกเว้นจะไม่เท่ากันก็ตาม) นี่เป็นข้อดีของกลไกข้อยกเว้นเหนือการใช้วิธีที่ส่งกลับรหัสข้อผิดพลาด นอกจากนี้ หลังจากข้อยกเว้นเกิดขึ้น ทิศทางของกระบวนการควบคุมก็ชัดเจนมากและจะไม่ทำให้กระบวนการสูญเสียการควบคุม
เพื่อให้ตัวอย่างการทำงานของข้อยกเว้น สมมติว่าเราต้องการเปิดไฟล์ในรูปแบบเฉพาะ:
ขั้นแรกให้กำหนดคลาสข้อยกเว้นสองคลาส (สืบทอดมาจากข้อยกเว้น)
EFileNotFound = คลาส (ข้อยกเว้น);
EFileFormatErr = คลาส (ข้อยกเว้น);
สมมติว่ามีปุ่มบน Form1 และการกดปุ่มจะเป็นการเปิดไฟล์:
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
เริ่ม
พยายาม
ToOpenFile();
ยกเว้น
บน EFileNotFound ทำ
ShowMessage('ขออภัย ไม่พบไฟล์');
onEFileFormatErr ทำ
ShowMessage('ขออภัย ไฟล์ไม่ใช่ไฟล์ที่ต้องการ');
บน E: ข้อยกเว้นทำ
ShowMessage(E.ข้อความ);
จบ;
จบ;
และฟังก์ชั่นในการเปิดไฟล์:
ขั้นตอน ToOpenFile;
varRetVal:จำนวนเต็ม;
เริ่ม
//โค้ดบางส่วนสำหรับ openfile
RetVal := -1; //เปิดล้มเหลว
ถ้า RetVal = 0 แล้ว //success
ออก
อย่างอื่นถ้า RetVal = -1 แล้ว
Raise EFileNotFound.Create('ไม่พบไฟล์')
อย่างอื่นถ้า RetVal = -2 แล้ว
Raise EFileFormatErr.Create ('ข้อผิดพลาดรูปแบบไฟล์')
อื่น ๆ // ข้อผิดพลาดอื่น ๆ
เพิ่มข้อยกเว้นสร้าง ('ข้อผิดพลาดที่ไม่รู้จัก');
จบ;
ในโปรแกรม TForm1.Button1Click เรียก ToOpenFile และตั้งค่าล่วงหน้าให้ลอง... ยกเว้นการจัดการข้อยกเว้นที่ ToOpenFile อาจถูกส่งออก แน่นอนว่าโค้ดการจัดการข้อยกเว้นของ TForm1.Button1Click สามารถทำให้ง่ายขึ้นได้:
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
เริ่ม
พยายาม
ToOpenFile();
ยกเว้น
ShowMessage('เปิดไฟล์ล้มเหลว');
จบ;
จบ;
การใช้ข้อยกเว้นช่วยแก้ปัญหาการใช้วิธีการที่ส่งคืนรหัสข้อผิดพลาด แน่นอนว่าการใช้ข้อยกเว้นนั้นไม่ได้ไม่มีค่าใช้จ่าย ข้อยกเว้นจะเพิ่มภาระให้กับโปรแกรม ดังนั้นจึงไม่แนะนำให้ใช้ข้อยกเว้นในทางที่ผิด มีความแตกต่างอย่างมากระหว่างการเขียนพยายามไม่กี่ครั้ง...ยกเว้น และเขียนพยายามนับพันครั้ง...ยกเว้น จากคำพูดของ Charlie Calverts: "คุณควรใช้ try...ยกเว้นบล็อก เมื่อดูเหมือนว่ามีประโยชน์ แต่พยายามอย่าตื่นเต้นกับเทคนิคนี้จนเกินไป"
นอกจากนี้ Object Pascal ยังแนะนำโครงสร้างลอง...สุดท้ายที่ไม่เหมือนใครอีกด้วย ฉันบอกไปแล้วว่าไม่มีความแตกต่างระหว่างการส่งคืนจากฟังก์ชันผ่านข้อยกเว้นและการส่งคืนจากฟังก์ชันตามปกติ ดังนั้นวัตถุในเครื่องในสแต็กในฟังก์ชันจะถูกปล่อยออกมาโดยอัตโนมัติ แต่วัตถุในฮีปจะไม่ถูกปล่อยออกมา อย่างไรก็ตาม โมเดลออบเจ็กต์ของ Object Pascal ขึ้นอยู่กับการอ้างอิง ซึ่งมีอยู่ในฮีป ไม่ใช่สแต็ก ดังนั้นบางครั้งเราจำเป็นต้องล้างทรัพยากรอ็อบเจ็กต์ในเครื่องก่อนที่จะส่งคืนจากฟังก์ชันผ่านข้อยกเว้น ลอง...ในที่สุดก็สามารถแก้ไขปัญหานี้ได้
ฉันเขียนโค้ด ToOpenFile ข้างต้นใหม่ คราวนี้ใช้ทรัพยากรบางอย่างในระหว่างกระบวนการ ToOpenFile และปล่อยทรัพยากรเหล่านี้หลังจากเกิดข้อยกเว้น (หรือไม่เกิดขึ้น) ก่อนที่จะกลับมาจากฟังก์ชัน:
ขั้นตอน ToOpenFile;
varRetVal: จำนวนเต็ม;
กระแส: TStream;
เริ่ม
//โค้ดบางส่วนสำหรับ openfile
สตรีม := TStream.Create;
RetVal := -1; //เปิดล้มเหลว
พยายาม
ถ้า RetVal = 0 แล้ว //success
ออก
อย่างอื่นถ้า RetVal = -1 แล้ว
Raise EFileNotFound.Create('ไม่พบไฟล์')
อย่างอื่นถ้า RetVal = -2 แล้ว
Raise EFileFormatErr.Create ('ข้อผิดพลาดรูปแบบไฟล์')
อื่น ๆ // ข้อผิดพลาดอื่น ๆ
เพิ่มข้อยกเว้นสร้าง ('ข้อผิดพลาดที่ไม่รู้จัก');
ในที่สุด
สตรีมฟรี;
จบ;
จบ;
เมื่ออ่านโค้ดข้างต้น เราจะเห็นว่าแม้ค่าของ RetVal จะเป็น 0 หลังจากดำเนินการ Exit แล้ว โค้ดในท้ายที่สุดจะยังคงถูกดำเนินการและส่งคืนจากฟังก์ชัน สิ่งนี้ทำให้แน่ใจได้ว่ามีการเปิดเผยทรัพยากรในท้องถิ่นอย่างถูกต้อง
วัตถุประสงค์และสถานการณ์การใช้งานของ try...ยกเว้น และ try...ในที่สุดก็แตกต่างกัน และผู้เริ่มต้นหลายคนสับสน ต่อไปนี้เป็นความรู้ส่วนตัวของผู้เขียน: โดยทั่วไปแล้ว try...ยกเว้น จะถูกใช้โดยผู้เรียกเพื่อจับข้อยกเว้นที่เกิดจากฟังก์ชันที่ถูกเรียกใช้และจัดการกับมัน และ try...finally โดยทั่วไปจะใช้สำหรับฟังก์ชันที่ส่งข้อยกเว้นเพื่อดำเนินการล้างข้อมูลทรัพยากรบางอย่าง
การเขียนโปรแกรมเชิงวัตถุจัดเตรียมโซลูชันการจัดการข้อผิดพลาดที่เรียกว่า "ข้อยกเว้น" หากใช้อย่างชาญฉลาดจะเป็นประโยชน์ต่องานของเราและสามารถปรับปรุงคุณภาพของโค้ดที่เราเขียนได้อย่างมาก
นิโครซอฟท์ ([email protected]) 2544.7.25
ที่มา: เอกสาร Sunistudio (http://www.sunistudio.com/asp/sunidoc.asp)