สิ่งใดก็ตามที่อยู่ในบล็อกในที่สุดก็จะถูกดำเนินการ (เกือบ) เสมอดังนั้นอะไรคือความแตกต่างระหว่างการวางโค้ดลงในโค้ดหรือปล่อยให้มันไม่ถูกเปิดเผย?
สิ่งใดก็ตามที่อยู่ในบล็อกในที่สุดก็จะถูกดำเนินการ (เกือบ) เสมอดังนั้นอะไรคือความแตกต่างระหว่างการวางโค้ดลงในโค้ดหรือปล่อยให้มันไม่ถูกเปิดเผย?
คำตอบ:
รหัสในบล็อกสุดท้ายจะถูกดำเนินการโดยไม่คำนึงว่ามีข้อยกเว้นหรือไม่ สิ่งนี้มีประโยชน์มากเมื่อพูดถึงฟังก์ชั่นทำความสะอาดบางอย่างที่คุณต้องใช้งานเสมอเช่นปิดการเชื่อมต่อ
ตอนนี้ฉันเดาคำถามของคุณว่าทำไมคุณควรทำสิ่งนี้:
try
{
doSomething();
}
catch
{
catchSomething();
}
finally
{
alwaysDoThis();
}
เมื่อคุณสามารถทำสิ่งนี้:
try
{
doSomething();
}
catch
{
catchSomething();
}
alwaysDoThis();
คำตอบคือหลาย ๆ ครั้งที่โค้ดในคำสั่ง catch ของคุณจะทำการ rethrow ข้อยกเว้นหรือแยกออกจากฟังก์ชั่นปัจจุบัน ด้วยรหัสหลัง "alwaysDoThis ();" การโทรจะไม่ดำเนินการหากรหัสภายในคำสั่ง catch ส่งคืนหรือส่งข้อยกเว้นใหม่
ข้อได้เปรียบส่วนใหญ่ของการลองใช้ก็ชี้ให้เห็นแล้ว แต่ฉันคิดว่าฉันจะเพิ่มสิ่งนี้:
try
{
// Code here that might throw an exception...
if (arbitraryCondition)
{
return true;
}
// Code here that might throw an exception...
}
finally
{
// Code here gets executed regardless of whether "return true;" was called within the try block (i.e. regardless of the value of arbitraryCondition).
}
พฤติกรรมนี้ทำให้มีประโยชน์มากในสถานการณ์ต่าง ๆ โดยเฉพาะอย่างยิ่งเมื่อคุณจำเป็นต้องทำการล้างข้อมูล (กำจัดทรัพยากร) แม้ว่าบล็อกการใช้มักจะดีกว่าในกรณีนี้
เมื่อใดก็ตามที่คุณใช้คำขอรหัสที่ไม่มีการจัดการเช่นตัวอ่านสตรีมคำขอฐานข้อมูล ฯลฯ และคุณต้องการที่จะรับข้อยกเว้นจากนั้นลองใช้ catch ในที่สุดและปิด stream ผู้อ่านข้อมูลและอื่น ๆ ในที่สุดถ้าคุณไม่ทำเมื่อข้อผิดพลาดการเชื่อมต่อไม่ได้ปิดนี่เป็นเรื่องไม่ดีกับคำขอ db
SqlConnection myConn = new SqlConnection("Connectionstring");
try
{
myConn.Open();
//make na DB Request
}
catch (Exception DBException)
{
//do somehting with exception
}
finally
{
myConn.Close();
myConn.Dispose();
}
หากคุณไม่ต้องการรับข้อผิดพลาดให้ใช้
using (SqlConnection myConn = new SqlConnection("Connectionstring"))
{
myConn.Open();
//make na DB Request
myConn.Close();
}
และวัตถุการเชื่อมต่อจะถูกกำจัดโดยอัตโนมัติหากมีข้อผิดพลาด แต่คุณไม่ได้จับข้อผิดพลาด
เพราะในที่สุดจะถูกประหารชีวิตแม้ว่าคุณจะไม่ได้จัดการกับข้อยกเว้นใน catch block
ในที่สุดงบสามารถดำเนินการได้แม้หลังจากกลับมา
private int myfun()
{
int a = 100; //any number
int b = 0;
try
{
a = (5 / b);
return a;
}
catch (Exception ex)
{
Response.Write(ex.Message);
return a;
}
// Response.Write("Statement after return before finally"); -->this will give error "Syntax error, 'try' expected"
finally
{
Response.Write("Statement after return in finally"); // --> This will execute , even after having return code above
}
Response.Write("Statement after return after finally"); // -->Unreachable code
}
finally
ในขณะที่:
try {
// do something risky
} catch (Exception ex) {
// handle an exception
} finally {
// do any required cleanup
}
เป็นโอกาสที่รับประกันได้ในการรันโค้ดหลังจากtry..catch
บล็อกของคุณไม่ว่าบล็อกการลองของคุณจะมีข้อยกเว้นหรือไม่
ที่ทำให้มันสมบูรณ์แบบสำหรับสิ่งต่าง ๆ เช่นการปล่อยทรัพยากรการเชื่อมต่อฐานข้อมูลจัดการไฟล์ ฯลฯ
ฉันจะอธิบายการใช้งานในที่สุดพร้อมกับข้อยกเว้นตัวอ่านไฟล์ตัวอย่าง
try{ StreamReader strReader = new StreamReader(@"C:\Ariven\Project\Data.txt"); Console.WriteLine(strReader.ReadeToEnd()); StreamReader.Close(); } catch (Exception ex) { Console.WriteLine(ex.Message); }
ในตัวอย่างด้านบนหากไฟล์ที่ชื่อว่าData.txtหายไปข้อยกเว้นจะถูกโยนทิ้งและจะได้รับการจัดการ แต่คำสั่งที่เรียกว่าStreamReader.Close();
จะไม่ถูกเรียกใช้งาน
เนื่องจากทรัพยากรนี้ที่เกี่ยวข้องกับผู้อ่านไม่เคยถูกปล่อยออกมา
StreamReader strReader = null; try{ strReader = new StreamReader(@"C:\Ariven\Project\Data.txt"); Console.WriteLine(strReader.ReadeToEnd()); } catch (Exception ex){ Console.WriteLine(ex.Message); } finally{ if (strReader != null){ StreamReader.Close(); } }
Happy Coding :)
หมายเหตุ: "@" ใช้เพื่อสร้างสตริงverbatimเพื่อหลีกเลี่ยงข้อผิดพลาดของ "escape escape ที่ไม่รู้จัก" สัญลักษณ์ @ หมายถึงการอ่านสตริงนั้นอย่างแท้จริงและไม่ตีความอักขระควบคุมเป็นอย่างอื่น
สมมติว่าคุณต้องตั้งค่าเคอร์เซอร์กลับไปที่ตัวชี้เริ่มต้นแทนเคอร์เซอร์ที่รอ (นาฬิกาทราย) หากมีข้อผิดพลาดเกิดขึ้นก่อนที่จะตั้งค่าเคอร์เซอร์และไม่ทำให้แอพเสียหายอย่างสมบูรณ์คุณอาจถูกเคอร์เซอร์ที่ทำให้เกิดความสับสน
บางครั้งคุณไม่ต้องการจัดการข้อยกเว้น (ไม่มี catch block) แต่คุณต้องการให้โค้ดการล้างข้อมูลดำเนินการ
ตัวอย่างเช่น:
try
{
// exception (or not)
}
finally
{
// clean up always
}
ในที่สุดบล็อกมีค่าสำหรับการล้างทรัพยากรใด ๆ ที่จัดสรรในบล็อกลองเช่นเดียวกับการเรียกใช้รหัสใด ๆ ที่ต้องดำเนินการแม้ว่าจะมีข้อยกเว้น การควบคุมจะถูกส่งผ่านไปยังบล็อกสุดท้ายเสมอ
อ่า ... ฉันคิดว่าฉันเห็นสิ่งที่คุณพูด! เอาฉันสักครู่ ... คุณกำลังสงสัยว่า "ทำไมวางไว้ในบล็อกในที่สุดแทนที่จะเป็นบล็อกหลังจากนั้นในที่สุดและอยู่นอกการลองจับในที่สุด"
ตัวอย่างเช่นอาจเป็นเพราะคุณหยุดการทำงานหากคุณเกิดข้อผิดพลาด แต่คุณยังต้องการล้างทรัพยากรเช่นเปิดไฟล์เชื่อมต่อฐานข้อมูลเป็นต้น
โฟลว์ควบคุมของบล็อกสุดท้ายคือหลังจากลองหรือจับบล็อก
[1. First Code]
[2. Try]
[3. Catch]
[4. Finally]
[5. After Code]
ด้วยข้อยกเว้น 1> 2> 3> 4> 5 ถ้า 3 มีคำสั่ง Return 1> 2> 3> 4
โดยไม่มีข้อยกเว้น 1> 2> 4> 5 ถ้า 2 มีคำสั่ง return 1> 2> 4
ตามที่ระบุไว้ในเอกสาร :
การใช้ catch และสุดท้ายร่วมกันคือการได้รับและใช้ทรัพยากรใน try block จัดการกับสถานการณ์พิเศษใน catch block และปล่อยทรัพยากรในบล็อกสุดท้าย
นอกจากนี้ยังควรอ่านสิ่งนี้ซึ่งระบุ:
เมื่อพบประโยค catch ที่ตรงกันระบบจะเตรียมการถ่ายโอนการควบคุมไปยังคำสั่งแรกของประโยค catch ก่อนที่จะเริ่มการทำงานของ catch clause จะเริ่มขึ้นระบบจะดำเนินการตามคำสั่งในที่สุดประโยคใด ๆ ที่เกี่ยวข้องกับคำสั่ง try จะซ้อนกันมากกว่าคำสั่ง catch
ดังนั้นจึงเป็นที่ชัดเจนว่ารหัสที่อยู่ในส่วนfinally
คำสั่งจะถูกดำเนินการแม้ว่าcatch
ข้อก่อนหน้านี้จะมีreturn
คำสั่ง