เหตุใดจึงต้องจับและสร้างข้อยกเว้นขึ้นใหม่ใน C #


557

ฉันกำลังดูบทความC # - วัตถุการถ่ายโอนข้อมูลบน DTO ที่ต่อเนื่องกันได้

บทความประกอบด้วยรหัสชิ้นนี้:

public static string SerializeDTO(DTO dto) {
    try {
        XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
        StringWriter sWriter = new StringWriter();
        xmlSer.Serialize(sWriter, dto);
        return sWriter.ToString();
    }
    catch(Exception ex) {
        throw ex;
    }
}

ส่วนที่เหลือของบทความดูมีเหตุผลและสมเหตุสมผล (สำหรับ noob) แต่การลองจับการขว้างเป็น WtfException ... นี่มันไม่เทียบเท่ากับการจัดการกับข้อยกเว้นหรือเปล่า?

Ergo:

public static string SerializeDTO(DTO dto) {
    XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
    StringWriter sWriter = new StringWriter();
    xmlSer.Serialize(sWriter, dto);
    return sWriter.ToString();
}

หรือฉันขาดพื้นฐานบางอย่างเกี่ยวกับการจัดการข้อผิดพลาดใน C # มันค่อนข้างจะเหมือนกับ Java (ลบด้วยการตรวจสอบข้อยกเว้น) ใช่ไหม? ... นั่นคือพวกเขาทั้งสองได้ปรับปรุง C ++

คำถามสแต็คโอเวอร์โฟลว์ความแตกต่างระหว่างการดักจับพารามิเตอร์น้อยกว่าการจับและไม่ทำอะไรเลย? ดูเหมือนว่าจะสนับสนุนการโต้แย้งของฉันว่าการลองจับการโยนไม่เป็นเรื่องจริง


แก้ไข:

เพียงเพื่อสรุปสำหรับทุกคนที่พบกระทู้นี้ในอนาคต ...

อย่า

try {
    // Do stuff that might throw an exception
}
catch (Exception e) {
    throw e; // This destroys the strack trace information!
}

ข้อมูลการติดตามสแต็กมีความสำคัญต่อการระบุสาเหตุของปัญหา!

ทำ

try {
    // Do stuff that might throw an exception
}
catch (SqlException e) {
    // Log it
    if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
        // Do special cleanup, like maybe closing the "dirty" database connection.
        throw; // This preserves the stack trace
    }
}
catch (IOException e) {
    // Log it
    throw;
}
catch (Exception e) {
    // Log it
    throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
    // Normal clean goes here (like closing open files).
}

ตรวจจับข้อยกเว้นเฉพาะเจาะจงมากขึ้นก่อนหน้าข้อ จำกัด ที่เฉพาะเจาะจงน้อยกว่า (เช่น Java)


อ้างอิง:


8
สรุปที่ดี คะแนนพิเศษสำหรับการรวมบล็อกในที่สุด
Fredrik Mörk

ฉันต้องการเพิ่มว่าคุณสามารถใช้ "การโยน" เพื่อให้มีประโยชน์มากยิ่งขึ้นโดยการเพิ่มพารามิเตอร์ที่ถูกส่งไปยังวิธีการในคอลเลกชัน e.Data ก่อน "throw;" คำสั่ง
Michael Bahig

@MickTheWarMachineDesigner (และจิตรกรนอกเวลา) ฮะ? คุณกำลังพูดถึงการจัดการกับ Microshite Suckwell (อาจเป็นไปได้ตั้งแต่ปี 2005 สำหรับทุกสิ่งที่ฉันรู้) ฉันกำลังพูดถึงการจัดการข้อยกเว้นโดยทั่วไป และใช่ฉันได้เรียนรู้บางอย่างตั้งแต่ที่ฉันโพสต์นี้เกือบสี่ปีที่ผ่านมา .... แต่ใช่ฉันขอสารภาพว่าคุณมีจุดที่ถูกต้อง แต่ฉันคิดว่าคุณพลาดจุดที่แท้จริง ถ้าคุณได้ดริฟท์ของฉัน คำถามนี้เกี่ยวกับการจัดการข้อยกเว้นทั่วไปใน C #; และเฉพาะเจาะจงมากขึ้นเกี่ยวกับการรื้อถอนข้อยกเว้น ... ทุกชนิด เย็น?
corlettk

โปรดพิจารณาย้ายหัวข้อสรุปการแก้ไขในคำถามของคุณเป็นคำตอบของมันเอง สำหรับเหตุผลที่ดูการแก้ไขตัวเองออกจากการตอบคำถามและคำตอบที่ฝังอยู่ในคำถาม
DavidRR

2
ทุกคนไม่ได้สังเกตเห็นส่วน "Excrement ที่เกิดขึ้น" หรือไม่? ดูเหมือนว่ารหัสไปเซ่อ!
Jason Loki Smith

คำตอบ:


430

ครั้งแรก; วิธีที่รหัสในบทความมันเป็นความชั่วร้าย throw exจะรีเซ็ต call stack ในข้อยกเว้นเป็นจุดที่คำสั่ง throw นี้คือ; สูญเสียข้อมูลเกี่ยวกับข้อยกเว้นที่สร้างขึ้นจริง

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

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

try 
{
    // code that may throw exceptions    
}
catch(Exception ex) 
{
    // add error logging here
    throw;
}

6
@ เฟรดริกเพียงแค่ fyi (แม้ว่าคุณอาจจะรู้) หากคุณไม่ได้ใช้exวัตถุนั้นก็ไม่จำเป็นต้องยกตัวอย่าง
Eoin Campbell

78
@Eoin: ถ้ามันไม่ได้ยกตัวอย่างมันค่อนข้างยากที่จะเข้าสู่ระบบ
Sam Axe

30
ใช่ฉันคิดว่า "ความชั่วร้าย" เป็นเรื่องที่ถูกต้อง ... พิจารณากรณีของข้อยกเว้นตัวชี้โมฆะโยนบางแห่งจากรหัสขนาดใหญ่ ข้อความคือวานิลลาโดยไม่มีการติดตามสแต็คที่คุณทิ้งไว้กับ "บางสิ่งบางอย่างเป็นโมฆะบางแห่ง" ไม่ดีเมื่อการผลิตตาย และคุณไม่ต้องใช้เวลาสักครู่เพื่อแก้ไขปัญหาของฟลามินต์และยกเลิกหรือแก้ไข ... การจัดการข้อยกเว้นที่ดีมีค่าเท่ากับน้ำหนักทองคำ
corlettk

4
เป็นจริงสำหรับ Java เช่นกัน ... "โยน" กับ "โยนอดีต"?
JasonStoltz

8
@ Jason ดูคำถามนี้ ใน Java throw exไม่ต้องรีสตาร์ทสแต็กสแต็ก
Matthew Flaschen

117

อย่าทำอย่างนี้

try 
{
...
}
catch(Exception ex)
{
   throw ex;
}

คุณจะสูญเสียข้อมูลการติดตามสแต็ก ...

ทั้งทำ

try { ... }
catch { throw; }

หรือ

try { ... }
catch (Exception ex)
{
    throw new Exception("My Custom Error Message", ex);
}

หนึ่งในเหตุผลที่คุณอาจต้องการสร้างใหม่คือถ้าคุณจัดการข้อยกเว้นต่าง ๆ เช่น

try
{
   ...
}
catch(SQLException sex)
{
   //Do Custom Logging 
   //Don't throw exception - swallow it here
}
catch(OtherException oex)
{
   //Do something else
   throw new WrappedException("Other Exception occured");
}
catch
{
   System.Diagnostics.Debug.WriteLine("Eeep! an error, not to worry, will be handled higher up the call stack");
   throw; //Chuck everything else back up the stack
}

7
ทำไมไม่ปล่อยให้จับ {ทิ้ง} ไปเลยล่ะ?
AnthonyWJones

3
ยังคงมีค่าในการออกจากการจับ {โยน; } ที่ด้านล่างของรายการประเภทจับข้อยกเว้นเฉพาะบนพื้นที่ที่พิสูจน์ผู้เขียนพิจารณาถึงกรณีแม้ว่าความคิดเห็นอาจพอเพียง การไม่คาดเดาเมื่อคุณอ่านรหัสเป็นสิ่งที่ดี
annakata

87
ด้วยเหตุผลบางอย่างชื่อ SQLException ทำให้ฉันรำคาญใจ
Michael Myers

13
การจับนั้น (ข้อยกเว้น) {โยนข้อยกเว้นใหม่ (... )} เป็นสิ่งที่คุณไม่ควรทำไม่เคยเคยทำเพราะคุณทำให้งงงวยข้อมูลข้อยกเว้นและทำการกรองข้อยกเว้นให้ไกลขึ้นโดยไม่จำเป็น ครั้งเดียวที่คุณควรตรวจจับข้อยกเว้นหนึ่งประเภทและโยนอีกประเภทหนึ่งคือเมื่อคุณใช้เลเยอร์ abstraction และคุณต้องแปลงประเภทข้อยกเว้นเฉพาะของผู้ให้บริการ (เช่น SqlException เทียบกับ XmlException) เป็นแบบทั่วไปมากขึ้น (เช่น DataLoadingException)
jammycakes

3
@dark_perfect คุณควรตรวจสอบอาร์กิวเมนต์นั้นล่วงหน้าที่จุดเริ่มต้นของเมธอดและโยน ArgumentNullException ที่นั่น (ไม่เร็ว)
Andrei Bozantan

56

C # (ก่อนหน้า C # 6) ไม่สนับสนุน CIL "ตัวกรองข้อยกเว้น" ซึ่ง VB ทำดังนั้นใน C # 1-5 เหตุผลหนึ่งในการโยนข้อยกเว้นอีกครั้งคือคุณไม่มีข้อมูลเพียงพอในเวลาที่จับ () เพื่อตรวจสอบว่าคุณต้องการตรวจจับข้อยกเว้นจริงหรือไม่

ตัวอย่างเช่นใน VB คุณสามารถทำได้

Try
 ..
Catch Ex As MyException When Ex.ErrorCode = 123
 .. 
End Try

... ซึ่งจะไม่จัดการ MyExceptions ด้วยค่า ErrorCode ที่แตกต่างกัน ใน C # ก่อนหน้า v6 คุณจะต้องจับและโยน MyException อีกครั้งหาก ErrorCode ไม่ใช่ 123:

try 
{
   ...
}
catch(MyException ex)
{
    if (ex.ErrorCode != 123) throw;
    ...
}

ตั้งแต่ C # 6.0 คุณสามารถกรองเหมือนกับ VB:

try 
{
  // Do stuff
} 
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
  // Handle, other exceptions will be left alone and bubble up
}

2
เดฟ แต่ (อย่างน้อยในจาวา) คุณจะไม่โยน MyException "ทั่วไป" คุณจะต้องกำหนดประเภทข้อยกเว้นเฉพาะแล้วโยนมันเพื่อให้มันแยกความแตกต่างโดยแยกในบล็อก catch ... แต่ใช่ ถ้าคุณไม่ใช่สถาปนิกของข้อยกเว้น (ฉันกำลังคิดว่า SQLException (Java อีกครั้ง) ของ JDBC ที่นี่ซึ่งน่าขยะแขยงทั่วไปและตีแผ่วิธีการ getErrorCode () ... อืมมคุณมีประเด็นมันก็แค่นั้น ฉันคิดว่ามีวิธีที่ดีกว่าที่จะทำถ้าเป็นไปได้ Cheers Mate ฉันขอขอบคุณเวลาของคุณมาก Keith.
corlettk

1
คำถามคือ "ทำไมจับและ rethrow ข้อยกเว้นใน C #?" และนี่คือคำตอบ =] ... และถึงแม้จะมีข้อยกเว้นพิเศษตัวกรองข้อยกเว้นก็สมเหตุสมผล: พิจารณากรณีที่คุณอยู่สมมติว่าจัดการกับ SqlTimeoutException และ SqlConnectionResetException ซึ่งเป็นทั้ง SqlException ตัวกรองข้อยกเว้นช่วยให้คุณสามารถจับ SqlException เฉพาะเมื่อเป็นหนึ่งในสองสิ่งนี้ดังนั้นแทนที่จะยุ่งเหยิงลองของคุณ / ด้วยการจัดการที่เหมือนกันสำหรับทั้งสองนี้คุณสามารถ "จับ SqlException ex เมื่อ ex คือ SqlTimeoutException หรือ ex คือ SqlConnectionResetException" (ฉันไม่ใช่ Dave btw)
bzlm

3
ข้อยกเว้นที่กรองมาใน C # 6!
Sir Crispalot

14

เหตุผลหลักของฉันที่มีรหัสเช่น:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

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

พวกมันดีในระหว่างการดีบั๊ก


1
ใช่ฉันจะจ่ายที่หนึ่ง แต่ใช่คุณไม่ต้องการที่จะเห็นว่าในการตีพิมพ์รหัส ... เพราะฉะนั้น: ฉันจะละอายใจที่จะเผยแพร่ ;-)
corlettk

25
ที่จริงแล้วสิ่งนี้ไม่จำเป็น - ใน Visual Studio คุณสามารถตั้งค่าดีบักเกอร์ให้แตกเมื่อมีการโยนข้อยกเว้นและจะแสดงรายละเอียดข้อยกเว้นในหน้าต่างตัวตรวจสอบสำหรับคุณ
jammycakes

8
หากคุณต้องการใช้รหัสบางอย่างเฉพาะในระหว่างการดีบักให้ใช้ #if DEBUG ... #endif และคุณไม่จำเป็นต้องลบบรรทัดเหล่านี้
Michael Freidgeim

1
ใช่ฉันทำไปแล้วสองสามครั้ง ทุกขณะนี้จากนั้นหนึ่งจะหลบหนีไปปล่อย @jammycakes ปัญหาเกี่ยวกับข้อยกเว้นของ Visual Studio คือบางครั้งข้อยกเว้นที่ฉันต้องการไม่ใช่การโยน (หรือประเภทเดียวเท่านั้น) ยังไม่ทราบเงื่อนไขจุดพักด้วย "break ถ้าข้ามโดยมีข้อยกเว้น" จนถึงตอนนี้สิ่งนี้จะยังคงมีประโยชน์ Michael Freidgeim: #if DEBUGรอบ ๆ ทั้งสองtry {และ} catch () {...}ค่อนข้างยุ่งเหยิงและตรงไปตรงมาทำให้ฉันไม่สบายใจ ... ตัวประมวลผลล่วงหน้าคือโดยทั่วไปแล้วจะพูดไม่ใช่เพื่อนของฉัน

11

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

public static string SerializeDTO(DTO dto) {
  try {
      XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
      StringWriter sWriter = new StringWriter();
      xmlSer.Serialize(sWriter, dto);
      return sWriter.ToString();
  }
  catch(Exception ex) {
    string message = 
      String.Format("Something went wrong serializing DTO {0}", DTO);
    throw new MyLibraryException(message, ex);
  }
}

ขอบคุณมากการห่อยกเว้น (โดยเฉพาะที่ถูกล่ามโซ่) นั้นมีสติอย่างสมบูรณ์แบบ ... สิ่งที่ไม่ได้สติก็คือข้อยกเว้นเพียงอย่างเดียวเพื่อให้คุณสามารถไล่ตามรอยแตกร้าวหรือแย่กว่านั้นกินมัน
corlettk

10

สิ่งนี้เทียบเท่ากับการไม่จัดการกับข้อยกเว้นทั้งหมดหรือไม่

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


8

คุณไม่ต้องการที่จะโยนอดีต - เช่นนี้จะสูญเสียการโทรสแต็ค ดูการจัดการข้อยกเว้น (MSDN)

และใช่การลอง ... catch ไม่ได้ทำสิ่งใดที่มีประโยชน์ (นอกเหนือจากการสูญเสียการเรียกซ้อน - ดังนั้นจริง ๆ แล้วแย่ลง - ยกเว้นด้วยเหตุผลบางอย่างที่คุณไม่ต้องการเปิดเผยข้อมูลนี้)


คุณจะไม่สูญเสีย call stack ทั้งหมดเมื่อคุณใช้ throw ex, คุณเพียงแค่สูญเสียบางส่วนของ call stack จากจุดที่เกิด exception ขึ้นสูงกว่า call stack แต่คุณยังคงสแตกการเรียกจากวิธีการที่ทำให้เกิดข้อยกเว้นลงไปยังตำแหน่งที่ไคลเอ็นต์เรียกใช้ อาจมีกรณีการใช้งานจริงที่คุณจะใช้มิฉะนั้นคนดีของ Microsoft จะไม่อนุญาต ที่กล่าวว่าฉันไม่ได้ใช้มัน อีกเรื่องที่ต้องจำคือการโยนข้อยกเว้นนั้นแพง ทำด้วยเหตุผลที่สมเหตุสมผลเท่านั้น เข้าสู่ระบบฉันจะคิดว่าจะสมควร ฯลฯ
ชาร์ลส์โอเว่น

5

ประเด็นที่ผู้คนไม่ได้กล่าวถึงคือในขณะที่ภาษา. NET ไม่ได้แยกความแตกต่างอย่างแท้จริงคำถามว่าควรดำเนินการเมื่อมีข้อยกเว้นเกิดขึ้นหรือไม่และจะแก้ไขได้จริงหรือไม่นั้นเป็นคำถามที่แตกต่างกันจริงๆ มีหลายกรณีที่เราควรดำเนินการตามข้อยกเว้นเราไม่มีความหวังในการแก้ไขและมีบางกรณีที่ทุกสิ่งที่จำเป็นในการ "แก้ไข" ข้อยกเว้นคือการคลายสแต็กจนถึงจุดหนึ่ง - ไม่จำเป็นต้องดำเนินการใด ๆ เพิ่มเติม .

เนื่องจากภูมิปัญญาทั่วไปที่เราควร "จับ" สิ่งที่เราสามารถ "จัดการ" ได้เท่านั้นโค้ดจำนวนมากที่ควรดำเนินการเมื่อมีข้อยกเว้นเกิดขึ้นไม่ได้ ตัวอย่างเช่นรหัสจำนวนมากจะได้รับการล็อคนำวัตถุที่ได้รับการปกป้อง "ชั่วคราว" เข้าสู่สถานะที่ละเมิดค่าคงที่จากนั้นนำวัตถุนั้นเข้าสู่สถานะที่ถูกต้องตามกฎหมายจากนั้นปล่อยการล็อกกลับก่อนที่บุคคลอื่นจะเห็นวัตถุ หากมีข้อยกเว้นเกิดขึ้นในขณะที่วัตถุอยู่ในสถานะที่ไม่ปลอดภัย - อันตรายการปฏิบัติทั่วไปคือการปลดล็อคโดยที่วัตถุยังอยู่ในสถานะนั้น รูปแบบที่ดีกว่ามากคือการมีข้อยกเว้นที่เกิดขึ้นในขณะที่วัตถุอยู่ในสภาพ "อันตราย" ทำให้การล็อคเป็นโมฆะอย่างชัดเจนดังนั้นความพยายามในอนาคตที่จะได้รับมันจะล้มเหลวทันที

ในภาษา. NET ส่วนใหญ่วิธีเดียวที่จะให้โค้ดดำเนินการตามข้อยกเว้นคือcatch(แม้ว่าจะรู้ว่ามันจะไม่สามารถแก้ไขข้อยกเว้นได้) ให้ดำเนินการกับปัญหาthrowตามที่ต้องการ อีกวิธีหนึ่งที่เป็นไปได้หากโค้ดไม่สนใจว่าข้อยกเว้นใดที่จะถูกโยนคือใช้okแฟล็กกับtry/finallyบล็อก ตั้งค่าokสถานะfalseก่อนหน้าบล็อกและtrueก่อนออกจากบล็อกและก่อนหน้าใด ๆreturnที่อยู่ภายในบล็อก จากนั้นภายในfinallyสมมติว่าหากokไม่ได้ตั้งค่าจะต้องมีข้อยกเว้นเกิดขึ้น วิธีการดังกล่าวดีกว่า a catch/ semantically throwแต่น่าเกลียดและบำรุงรักษาน้อยกว่าที่ควรจะเป็น


5

สิ่งนี้มีประโยชน์เมื่อฟังก์ชันการเขียนโปรแกรมของคุณสำหรับไลบรารีหรือ dll

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

ฉันคิดว่านี่ใช้เพียงเพื่อให้ข้อยกเว้นที่ถูกโยนนั้นสะอาดและไม่เข้าไปใน "ราก" ของห้องสมุด


3

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


ฉันไม่ได้สิ่งที่คุณอยู่จนกว่าฉันจะอ่านลิงก์ ... และฉันก็ยังไม่แน่ใจว่าคุณกำลังทำอะไรอยู่ ... ฉันไม่คุ้นเคยกับ VB.NET โดยสิ้นเชิง ฉันคิดว่ามันส่งผลให้มีการรายงานผลรวมว่า "ไม่สอดคล้องกัน" ใช่ไหม ... ฉันเป็นแฟนตัวยงของวิธีการคงที่ .. นอกเหนือจากพวกเขาเป็นเรื่องง่ายมีโอกาสน้อยที่ไม่สอดคล้องกันถ้าคุณแยกการตั้งค่าคุณลักษณะจากรหัส ซึ่งทำงานจริง สแต็คคือ "การชำระด้วยตนเอง"
corlettk

3
ผู้คนคาดหวังว่าเมื่อพวกเขาเขียน "ลอง {Foo ();} ในที่สุด {Bar ();}" ที่ไม่มีอะไรทำงานระหว่าง Foo และ Bar แต่นี่ไม่เป็นความจริง หากผู้โทรของคุณเพิ่มตัวกรองข้อยกเว้นและไม่มีการ 'จับ' และ Foo () จะส่งสัญญาณรหัสสุ่มอื่น ๆ จากผู้โทรของคุณจะทำงานก่อนที่แถบสุดท้ายของคุณจะทำงาน สิ่งนี้แย่มากถ้าคุณทำลายค่าคงที่หรือความปลอดภัยที่เพิ่มขึ้นโดยคาดว่าในที่สุดพวกเขาจะ 'คืน' ให้กลับคืนสู่สภาพปกติในที่สุดและจะไม่มีรหัสอื่นใดที่จะเห็นการเปลี่ยนแปลงชั่วคราว
Brian

3

มันขึ้นอยู่กับสิ่งที่คุณกำลังทำใน catch block และถ้าคุณต้องการส่งข้อผิดพลาดไปยังรหัสการโทรหรือไม่

คุณอาจพูดCatch io.FileNotFoundExeption exแล้วใช้เส้นทางของไฟล์ทางเลือกหรือบางอย่าง แต่ยังคงมีข้อผิดพลาด

นอกจากนี้การทำThrowแทนที่จะThrow Exช่วยให้คุณสามารถติดตามสแต็กเต็ม Throw ex รีสตาร์ทการติดตามสแต็กจากข้อความ Throw (ฉันหวังว่าเหมาะสม)


3

ในขณะที่คำตอบอื่น ๆ อีกมากมายให้ตัวอย่างที่ดีว่าทำไมคุณอาจต้องการที่จะตรวจสอบข้อยกเว้นใหม่ดูเหมือนว่าไม่มีใครพูดถึงสถานการณ์ 'ในที่สุด'

ตัวอย่างนี้เป็นที่ที่คุณมีวิธีการที่คุณตั้งค่าเคอร์เซอร์ (ตัวอย่างเช่นเคอร์เซอร์รอ) วิธีการมีหลายจุดทางออก (เช่นถ้า () กลับ;) และคุณต้องการให้แน่ใจว่าเคอร์เซอร์ถูกรีเซ็ตที่ สิ้นสุดของวิธีการ

ในการทำเช่นนี้คุณสามารถล้อมโค้ดทั้งหมดในแบบลอง / จับ / ในที่สุด ในที่สุดการตั้งค่าเคอร์เซอร์กลับไปที่เคอร์เซอร์ขวา เพื่อที่คุณจะได้ไม่ฝังข้อยกเว้นใด ๆ ที่ถูกต้องให้ทำการขุดขึ้นมาใหม่

try
{
    Cursor.Current = Cursors.WaitCursor;
    // Test something
    if (testResult) return;
    // Do something else
}
catch
{
    throw;
}
finally
{
     Cursor.Current = Cursors.Default;
}

1
เป็นcatchส่วนที่จำเป็นของtry...finallyประวัติศาสตร์หรือว่ามันมีบทบาทหน้าที่ในตัวอย่างนี้หรือไม่? - ฉันเพิ่งตรวจสอบอีกครั้งและฉันสามารถใช้งานได้try {} finally {}โดยไม่ต้องมี catch catch เลย
Sebi

2

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

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

ในการทำซ้ำอีกครั้งแม้ว่าจะไม่มีจุดในการตรวจจับข้อยกเว้นในตัวอย่างที่คุณโพสต์ อย่าทำอย่างนั้น!


1

ขออภัยตัวอย่างมากมายเช่น "การออกแบบที่ปรับปรุง" ยังคงมีกลิ่นอย่างน่ากลัวหรืออาจทำให้เข้าใจผิดอย่างยิ่ง ต้องลอง {} catch {log; การโยน} นั้นไร้สาระอย่างที่สุด การบันทึกข้อยกเว้นควรทำในตำแหน่งกึ่งกลางภายในแอปพลิเคชัน ข้อยกเว้นทำให้สแต็คของแทร็คเทรซทำไมไม่ลองบันทึกมันไว้ที่อื่นและใกล้กับขอบของระบบ?

ควรใช้ข้อควรระวังเมื่อคุณทำให้บริบทของคุณเป็นอนุกรม (เช่น DTO ในตัวอย่างหนึ่งที่กำหนด) เพียงลงในข้อความบันทึก มันสามารถมีข้อมูลที่ละเอียดอ่อนซึ่งอาจไม่ต้องการเข้าถึงมือของทุกคนที่สามารถเข้าถึงไฟล์บันทึกได้ และถ้าคุณไม่เพิ่มข้อมูลใหม่ลงในข้อยกเว้นฉันไม่เห็นจุดตัดข้อยกเว้นจริงๆ Java แบบเก่าที่ดีมีบางจุดที่ต้องการให้ผู้เรียกทราบว่ามีข้อยกเว้นประเภทใดที่ควรคาดหวังจากนั้นจึงเรียกใช้รหัส เนื่องจากคุณไม่มีสิ่งนี้ใน. NET การตัดคำไม่ได้ดีในกรณีอย่างน้อย 80% ที่ฉันเคยเห็น


ขอบคุณสำหรับความคิดของคุณโจ ใน Java (และ C # ฉันคิดว่า) ฉันชอบที่จะเห็นคำอธิบายประกอบในระดับชั้น @FaultBoundary ซึ่งบังคับให้มีข้อยกเว้นทั้งหมด (รวมถึงประเภทข้อยกเว้นที่ไม่ถูกตรวจสอบ) ที่จะถูกโยนหรือถูกประกาศให้เป็นโยน ฉันจะใช้คำอธิบายประกอบนี้ในส่วนต่อประสานสาธารณะของแต่ละชั้นสถาปัตยกรรม ดังนั้นอินเตอร์เฟส @FaultBoundary ThingDAO จะไม่สามารถรั่วรายละเอียดการใช้งานเช่น SQLExceptions, NPE's หรือ AIOB แต่สแต็ค "สาเหตุ" จะถูกบันทึกไว้และ DAOSystemException จะถูกโยนทิ้ง ... ฉันกำหนดข้อยกเว้นของระบบเป็น "ร้ายแรงอย่างถาวร"
corlettk

5
มีเหตุผลมากมายที่จะจับเข้าสู่ระบบจากนั้นทำการขึ้นใหม่อีกครั้ง โดยเฉพาะอย่างยิ่งถ้าวิธีการที่มีการบันทึกการจับมีข้อมูลที่คุณสูญเสียเมื่อคุณออกจากวิธีการ ข้อผิดพลาดอาจได้รับการจัดการในภายหลัง แต่ไม่ได้บันทึกและคุณสูญเสียข้อมูลเกี่ยวกับข้อบกพร่องในระบบ
Andy

1
นี่คือที่ซึ่งคุณสมบัติข้อมูลของคลาส Exception นั้นสะดวก - เก็บข้อมูลท้องถิ่นทั้งหมดนั้นสำหรับการบันทึกทั่วไป เดิมบทความนี้มาถึงความสนใจของฉัน: blog.abodit.com/2010/03/…
McGuireV10

1

นอกจากสิ่งที่คนอื่นพูดแล้วดูคำตอบของฉันสำหรับคำถามที่เกี่ยวข้องซึ่งแสดงให้เห็นว่าการจับและการทำใหม่ไม่ใช่ไม่ใช่ op (เป็นใน VB แต่รหัสบางตัวอาจถูกเรียกใช้จาก C # จาก VB)


แม้ว่าลิงก์นี้อาจตอบคำถามได้ดีกว่าหากรวมส่วนสำคัญของคำตอบไว้ที่นี่และให้ลิงก์สำหรับการอ้างอิง คำตอบสำหรับลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าเว็บที่เชื่อมโยงมีการเปลี่ยนแปลง - จากการรีวิว
LHIOUI

@HamzaLH ฉันเห็นด้วยว่ามันไม่ใช่คำตอบที่เขียนได้ดี แต่มีข้อมูลที่แตกต่างจากคำตอบอื่น ๆ และคะแนนโหวตเป็นบวก ดังนั้นฉันไม่เข้าใจทำไมคุณแนะนำให้ลบ “ คำตอบสั้น ๆ ที่อยู่ในหัวข้อและการแก้ปัญหายังคงเป็นคำตอบ” จาก meta.stackexchange.com/questions/226258/ …
Michael Freidgeim

นี่คือคำตอบสำหรับลิงก์เท่านั้น
LHIOUI

1. คำตอบสำหรับลิงก์อย่างเดียวควรเปลี่ยนเป็นความคิดเห็นไม่ใช่ลบ 2. เป็นการอ้างอิงถึงคำถาม SO อื่น ๆ ไม่ใช่ไปยังไซต์ภายนอกซึ่งถือว่ามีโอกาสน้อยที่จะถูกทำลายเมื่อเวลาผ่านไป 3. มีคำอธิบายเพิ่มเติมซึ่งทำให้ไม่ใช่ "ลิงก์เท่านั้น" - ดูmeta.stackexchange.com/questions/225370/…
Michael Freidgeim

1

คำตอบส่วนใหญ่พูดถึงสถานการณ์ catch-log-rethrow

แทนที่จะเขียนในรหัสของคุณให้พิจารณาใช้ AOP โดยเฉพาะPostsharp.Diagnostic.Toolkitด้วย OnExceptionOptions IncludeParameterValue และ IncludeThisArgument


แม้ว่าลิงก์นี้อาจตอบคำถามได้ดีกว่าหากรวมส่วนสำคัญของคำตอบไว้ที่นี่และให้ลิงก์สำหรับการอ้างอิง คำตอบสำหรับลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าเว็บที่เชื่อมโยงมีการเปลี่ยนแปลง - จากการรีวิว
Tony Dong

@TonyDong ฉันยอมรับว่ามันไม่ใช่คำตอบที่เป็นลายลักษณ์อักษร แต่มีข้อมูลที่แตกต่างจากคำตอบอื่น ๆ และคะแนนโหวตเป็นบวก ดังนั้นฉันไม่เข้าใจทำไมคุณแนะนำให้ลบ BTW ลิงค์ 5 ปีต่อมายังคงใช้ได้ “ คำตอบสั้น ๆ ที่อยู่ในหัวข้อและการแก้ปัญหายังคงเป็นคำตอบ” จาก meta.stackexchange.com/questions/226258/…
Michael Freidgeim

Stackoverflow มีคำแนะนำนี้เท่านั้น
Tony Dong

@TonyDong หากคำตอบนั้นไม่ไร้ประโยชน์จริงๆคุณควรเลือก“ ดูดี”
Michael Freidgeim

0

Rethrowing ข้อยกเว้นผ่านทาง throwจะมีประโยชน์เมื่อคุณไม่มีรหัสเฉพาะในการจัดการข้อยกเว้นปัจจุบันหรือในกรณีที่คุณมีตรรกะในการจัดการกรณีข้อผิดพลาดเฉพาะ แต่ต้องการข้ามคนอื่น ๆ ทั้งหมด

ตัวอย่าง:

string numberText = "";
try
{
    Console.Write("Enter an integer: ");
    numberText = Console.ReadLine();
    var result = int.Parse(numberText);

    Console.WriteLine("You entered {0}", result);
}
catch (FormatException)
{
    if (numberText.ToLowerInvariant() == "nothing")
    {
        Console.WriteLine("Please, please don't be lazy and enter a valid number next time.");
    }
    else
    {
        throw;
    }
}    
finally
{
    Console.WriteLine("Freed some resources.");
}
Console.ReadKey();

อย่างไรก็ตามยังมีวิธีอื่นในการทำเช่นนี้โดยใช้คำสั่งแบบมีเงื่อนไขในบล็อก catch:

string numberText = "";
try
{
    Console.Write("Enter an integer: ");
    numberText = Console.ReadLine();
    var result = int.Parse(numberText);

    Console.WriteLine("You entered {0}", result);
}
catch (FormatException) when (numberText.ToLowerInvariant() == "nothing")
{
    Console.WriteLine("Please, please don't be lazy and enter a valid number next time.");
}    
finally
{
    Console.WriteLine("Freed some resources.");
}
Console.ReadKey();

กลไกนี้มีประสิทธิภาพมากกว่าการโยนข้อยกเว้นอีกครั้งเนื่องจากการรันไทม์. NET ไม่จำเป็นต้องสร้างวัตถุข้อยกเว้นก่อนที่จะทำการโยนอีกครั้ง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.