ความแตกต่างระหว่าง try / catch / throw และ try / catch (e) / throw e


103

อะไรคือความแตกต่างระหว่าง

try { }
catch
{ throw; }

และ

try { }
catch(Exception e)
{ throw e;}

เหรอ?

และควรใช้เมื่อใด

คำตอบ:


151

การก่อสร้าง

try { ... }
catch () { ... } /* You can even omit the () here */

try { ... }
catch (Exception e) { ... }

มีความคล้ายคลึงกันตรงที่ทั้งสองจะจับทุกข้อยกเว้นที่โยนเข้าไปในtryบล็อก (และควรหลีกเลี่ยงเว้นแต่คุณจะใช้สิ่งนี้เพื่อบันทึกข้อยกเว้นเท่านั้น) ตอนนี้ดูสิ่งเหล่านี้:

try { ... }
catch ()
{
    /* ... */
    throw;
}

try { ... }
catch (Exception e)
{
    /* ... */
    throw;
}

try { ... }
catch (Exception e)
{
    /* ... */
    throw e;
}

บล็อกการลองจับครั้งแรกและครั้งที่สองนั้นเหมือนกันทุกประการเพียงแค่สร้างข้อยกเว้นปัจจุบันขึ้นมาใหม่และข้อยกเว้นนั้นจะเก็บ "แหล่งที่มา" และการติดตามสแต็กไว้

บล็อกลองจับที่สามแตกต่างกัน เมื่อมันพ่นข้อยกเว้นมันจะเปลี่ยนแหล่งที่มาและการติดตามสแต็กเพื่อให้ดูเหมือนว่าข้อยกเว้นถูกโยนออกไปจากวิธีนี้จากบรรทัดนั้นthrow eบนเมธอดที่มีบล็อก try-catch นั้น

คุณควรใช้อันไหน? จริงๆมันขึ้นอยู่กับแต่ละกรณี

สมมติว่าคุณมีไฟล์ Personคลาสที่มี.Save()วิธีการที่จะคงอยู่ในฐานข้อมูล สมมติว่าแอปพลิเคชันของคุณดำเนินการตามPerson.Save()วิธีการใดที่หนึ่ง หากฐานข้อมูลของคุณปฏิเสธที่จะช่วยชีวิตบุคคลนั้น.Save()จะทำให้เกิดข้อยกเว้น คุณควรใช้throwหรือthrow eในกรณีนี้? มันขึ้นอยู่กับ

สิ่งที่ฉันชอบคือทำ:

try {
    /* ... */
    person.Save();
}
catch(DBException e) {
    throw new InvalidPersonException(
       "The person has an invalid state and could not be saved!",
       e);
}

สิ่งนี้ควรทำให้ DBException เป็น "Inner Exception" ของข้อยกเว้นที่ใหม่กว่าที่กำลังโยน ดังนั้นเมื่อคุณตรวจสอบ InvalidPersonException นี้การติดตามสแต็กจะมีข้อมูลกลับไปยังเมธอด Save (ซึ่งอาจเพียงพอสำหรับคุณในการแก้ปัญหา) แต่คุณยังคงสามารถเข้าถึงข้อยกเว้นเดิมได้หากคุณต้องการ

ในคำพูดสุดท้ายเมื่อคุณคาดว่าจะมีข้อยกเว้นคุณควรจับข้อยกเว้นเฉพาะข้อนั้นจริงๆไม่ใช่ข้อยกเว้นทั่วไปExceptionกล่าวคือหากคุณคาดหวัง InvalidPersonException คุณควรต้องการ:

try { ... }
catch (InvalidPersonException e) { ... }

ถึง

try { ... }
catch (Exception e) { ... }

โชคดี!


34

ขั้นแรกจะรักษาการติดตามสแต็กในขณะที่วินาทีรีเซ็ต ซึ่งหมายความว่าหากคุณใช้แนวทางที่สองการติดตามสแต็กของข้อยกเว้นจะเริ่มต้นจากวิธีนี้เสมอและคุณจะสูญเสียการติดตามข้อยกเว้นดั้งเดิมซึ่งอาจเป็นหายนะสำหรับคนที่อ่านบันทึกข้อยกเว้นเนื่องจากเขาจะไม่พบสาเหตุดั้งเดิมของข้อยกเว้น .

แนวทางที่สองอาจเป็นประโยชน์เมื่อคุณต้องการเพิ่มข้อมูลเพิ่มเติมในการติดตามสแต็ก แต่จะใช้ในลักษณะนี้:

try
{
    // do something
}
catch (Exception ex)
{
    throw new Exception("Additional information...", ex);
}

มีบล็อกโพสต์เกี่ยวกับความแตกต่าง


นั่นคือสิ่งที่ควรรู้!
Myles

แล้วทำไมต้องใช้ครั้งที่สองล่ะ? ใช้แค่อันแรกดีกว่าไหม
Karim

1
ข้อที่สองมีประโยชน์เมื่อคุณต้องการตรวจสอบข้อยกเว้นที่เฉพาะเจาะจง - OutOfRangeException อยู่ในใจ - หรือต้องการบันทึกข้อความเป็นต้นข้อแรกดูเหมือนจะเป็นตัวจัดการข้อยกเว้นสัญลักษณ์แทนคล้ายกับลอง {} catch (... ) {} ใน c ++
3Dave

1
David ซึ่งใช้กับส่วนที่จับได้(Exception e)เท่านั้น และที่แยกต่างหากจากVSthrow throw e
Henk Holterman

6

คุณควรใช้

try { }
catch(Exception e)
{ throw }

หากคุณต้องการทำบางสิ่งโดยมีข้อยกเว้นก่อนที่จะโยนมันอีกครั้ง (เช่นบันทึกข้อมูล) การโยนแบบโดดเดี่ยวจะรักษาร่องรอยของสแต็ก


และจะเกิดอะไรขึ้นถ้าฉันแทนที่ "โยน" ตรงนี้ด้วย "โยน e"
Karim

5

ความแตกต่างระหว่างตัวจับแบบไม่มีพารามิเตอร์และ catch(Exception e)คือคุณได้รับการอ้างอิงถึงข้อยกเว้น จากเฟรมเวิร์กเวอร์ชัน 2 ที่ไม่มีการจัดการข้อยกเว้นจะรวมอยู่ในข้อยกเว้นที่มีการจัดการดังนั้นข้อยกเว้นที่ไม่มีพารามิเตอร์จะไม่มีประโยชน์สำหรับสิ่งใด ๆ อีกต่อไป

ความแตกต่างระหว่างthrow;และthrow e;คือข้อแรกใช้เพื่อลบข้อยกเว้นใหม่และข้อที่สองใช้เพื่อโยนข้อยกเว้นที่สร้างขึ้นใหม่ หากคุณใช้ข้อยกเว้นที่สองเพื่อสร้างข้อยกเว้นขึ้นมาใหม่จะถือว่าเป็นข้อยกเว้นใหม่และแทนที่ข้อมูลสแต็กทั้งหมดจากที่ที่เคยถูกโยนทิ้งไป

ดังนั้นคุณไม่ควรใช้ทางเลือกใดทางเลือกหนึ่งในคำถาม คุณไม่ควรใช้ตัวจับแบบไม่มีพารามิเตอร์และคุณควรใช้throw;เพื่อลบข้อยกเว้นใหม่

นอกจากนี้ในกรณีส่วนใหญ่คุณควรใช้คลาสข้อยกเว้นที่เฉพาะเจาะจงมากกว่าคลาสพื้นฐานสำหรับข้อยกเว้นทั้งหมด คุณควรจับเฉพาะข้อยกเว้นที่คุณคาดการณ์ไว้เท่านั้น

try {
   ...
} catch (IOException e) {
   ...
   throw;
}

หากคุณต้องการเพิ่มข้อมูลใด ๆ เมื่อสร้างข้อยกเว้นใหม่ให้สร้างข้อยกเว้นใหม่โดยให้ข้อยกเว้นเดิมเป็นข้อยกเว้นภายในเพื่อเก็บรักษาข้อมูลทั้งหมด:

try {
   ...
} catch (IOException e) {
   ...
   throw new ApplicationException("Some informative error message", e);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.