อะไรคือความแตกต่างระหว่าง
try { ... }
catch{ throw }
และ
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
ไม่ว่าข้อความที่สองจะแสดงข้อความหรือไม่?
อะไรคือความแตกต่างระหว่าง
try { ... }
catch{ throw }
และ
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
ไม่ว่าข้อความที่สองจะแสดงข้อความหรือไม่?
คำตอบ:
throw; rethrows ข้อยกเว้นเดิมและเก็บสแต็กการติดตามดั้งเดิม
throw ex;ส่งข้อยกเว้นดั้งเดิม แต่รีเซ็ตการติดตามสแต็กทำลายข้อมูลการติดตามสแต็กทั้งหมดจนกว่าcatchบล็อกของคุณ
throw ex;throw new Exception(ex.Message);ยิ่งเลวร้ายลง มันสร้างExceptionอินสแตนซ์ใหม่เอี่ยมสูญเสียการติดตามสแต็กดั้งเดิมของข้อยกเว้นรวมถึงชนิดของมัน (เช่น, IOException)
นอกจากนี้ข้อยกเว้นบางอย่างมีข้อมูลเพิ่มเติม (เช่นArgumentException.ParamName)
throw new Exception(ex.Message); จะทำลายข้อมูลนี้ด้วย
ในบางกรณีคุณอาจต้องการรวมข้อยกเว้นทั้งหมดในวัตถุข้อยกเว้นที่กำหนดเองเพื่อให้คุณสามารถให้ข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่รหัสกำลังทำอยู่เมื่อมีการโยนข้อยกเว้น
การทำเช่นนี้กำหนดคลาสใหม่ที่สืบทอดException, เพิ่มทั้งสี่ก่อสร้างยกเว้นและเลือกนวกรรมิกเพิ่มเติมที่จะใช้เวลาInnerExceptionเช่นเดียวกับการเพิ่มเติมข้อมูลและโยนระดับยกเว้นใหม่ของคุณผ่านexเป็นInnerExceptionพารามิเตอร์ โดยการส่งต้นฉบับInnerExceptionคุณรักษาคุณสมบัติของข้อยกเว้นดั้งเดิมทั้งหมดรวมถึงการติดตามสแต็ก
throw new MyCustomException(myMessage, ex);ของหลักสูตร
ex.Messageซึ่งเป็นที่เลวร้ายยิ่ง
[Serializable()]ด้วย
throw; throw;คุณแนะนำให้จัดการกับสิ่งนั้นอย่างไร stackoverflow.com/questions/2493779/…
วิธีแรกคือรักษา stacktrace ดั้งเดิม:
try { ... }
catch
{
// Do something.
throw;
}
ที่สองช่วยให้คุณสามารถเปลี่ยนประเภทของข้อยกเว้นและ / หรือข้อความและข้อมูลอื่น ๆ :
try { ... } catch (Exception e)
{
throw new BarException("Something broke!");
}
นอกจากนี้ยังมีวิธีที่สามที่คุณผ่านข้อยกเว้นภายใน:
try { ... }
catch (FooException e) {
throw new BarException("foo", e);
}
ฉันขอแนะนำให้ใช้:
อีกประเด็นหนึ่งที่ฉันไม่เห็นใครทำ:
หากคุณไม่ทำอะไรเลยในบล็อก catch {} การลอง ... catch ไม่มีจุดหมาย ฉันเห็นสิ่งนี้ตลอดเวลา:
try
{
//Code here
}
catch
{
throw;
}
หรือแย่กว่านั้น:
try
{
//Code here
}
catch(Exception ex)
{
throw ex;
}
แย่ที่สุด:
try
{
//Code here
}
catch(Exception ex)
{
throw new System.Exception(ex.Message);
}
throwโยนข้อยกเว้นที่ตรวจพบอีกครั้งกลับไปที่การติดตามสแต็กในขณะที่throw new Exceptionสูญเสียรายละเอียดบางส่วนของข้อยกเว้นที่ตรวจพบ
ตามปกติคุณจะใช้throwตัวเองเพื่อบันทึกข้อยกเว้นโดยไม่ต้องจัดการอย่างเต็มที่ ณ จุดนั้น
BlackWasp ได้เป็นบทความที่ดีพอบรรดาศักดิ์ข้อยกเว้นการขว้างปาใน C #
การขว้างข้อยกเว้นใหม่จะทำให้การติดตามสแต็กปัจจุบันหายไป
throw;จะเก็บสแต็กการติดตามดั้งเดิมไว้และจะมีประโยชน์มากกว่า ข้อยกเว้นของกฎนั้นคือเมื่อคุณต้องการสรุปข้อยกเว้นในข้อยกเว้นที่กำหนดเองของคุณเอง จากนั้นคุณควรทำ:
catch(Exception e)
{
throw new CustomException(customMessage, e);
}
throwมีไว้สำหรับการรื้อใหม่ข้อยกเว้นที่ตรวจพบ สิ่งนี้มีประโยชน์หากคุณต้องการทำบางสิ่งด้วยข้อยกเว้นก่อนที่จะส่งต่อสายการโทร
การใช้throwโดยไม่มีข้อโต้แย้งใด ๆ รักษาสแต็คการโทรสำหรับการดีบัก
หากคุณต้องการคุณสามารถโยนข้อยกเว้นใหม่โดยชุดเดิมเป็นข้อยกเว้นภายใน
ตัวอย่างที่สองของคุณจะรีเซ็ตการติดตามสแต็กของข้อยกเว้น ครั้งแรกที่ถูกต้องที่สุดรักษาต้นกำเนิดของข้อยกเว้น นอกจากนี้คุณยังได้แกะแบบดั้งเดิมซึ่งเป็นกุญแจสำคัญในการรู้ว่าอะไรผิดพลาดจริง ๆ ... ถ้าจำเป็นต้องใช้ฟังก์ชันที่สองเช่นเพิ่มข้อมูลเพิ่มเติมหรือห่อใหม่ด้วยประเภทพิเศษเช่นกำหนดเอง 'HandleableException' ตรวจสอบให้แน่ใจว่ามีการตั้งค่าคุณสมบัติ InnerException ด้วย!
ความแตกต่างที่สำคัญที่สุดคือนิพจน์ที่สองลบประเภทข้อยกเว้น และประเภทการยกเว้นมีบทบาทสำคัญในการตรวจจับข้อยกเว้น:
public void MyMethod ()
{
// both can throw IOException
try { foo(); } catch { throw; }
try { bar(); } catch(E) {throw new Exception(E.message); }
}
(...)
try {
MyMethod ();
} catch (IOException ex) {
Console.WriteLine ("Error with I/O"); // [1]
} catch (Exception ex) {
Console.WriteLine ("Other error"); // [2]
}
ถ้าfoo()โยนIOException, [1]จับบล็อกจะจับข้อยกเว้น แต่เมื่อbar()โยนIOExceptionมันจะถูกแปลงเป็นExceptionมดธรรมดาจะไม่ถูกจับโดย[1]บล็อกจับ
โยนหรือโยนอดีตทั้งสองใช้ในการโยนหรือ rethrow ข้อยกเว้นเมื่อคุณเพียงแค่บันทึกข้อมูลข้อผิดพลาดและไม่ต้องการส่งข้อมูลใด ๆ กลับไปยังผู้โทรคุณเพียงแค่บันทึกข้อผิดพลาดในการจับและออก แต่ในกรณีที่คุณต้องการส่งข้อมูลที่มีความหมายเกี่ยวกับข้อยกเว้นไปยังผู้โทรที่คุณใช้การโยนหรือการส่งข้อความ ตอนนี้ความแตกต่างระหว่าง Throw and Throw Ex คือ Throw เก็บการติดตามสแต็กและข้อมูลอื่น ๆ แต่การโยน ex สร้างวัตถุข้อยกเว้นใหม่และด้วยเหตุนี้การติดตามสแต็กเดิมจะหายไป ดังนั้นเมื่อเราควรใช้ Throw and Throw e ยังมีบางสถานการณ์ที่คุณอาจต้องการสร้างข้อยกเว้นใหม่เพื่อรีเซ็ตข้อมูลสแต็กการโทร ตัวอย่างเช่นหากวิธีอยู่ในไลบรารีและคุณต้องการซ่อนรายละเอียดของไลบรารีจากรหัสการโทร คุณไม่ต้องการให้ call stack รวมข้อมูลเกี่ยวกับเมธอดส่วนตัวภายในไลบรารี ในกรณีดังกล่าวคุณสามารถตรวจจับข้อยกเว้นในวิธีการสาธารณะของไลบรารีและจากนั้นทำการสร้างใหม่เพื่อให้การเรียกสแต็คเริ่มต้นด้วยวิธีสาธารณะ
โยน; จัดเรียงข้อยกเว้นดั้งเดิมใหม่และเก็บประเภทข้อยกเว้น
โยนข้อยกเว้นใหม่ (); จัดเรียงชนิดข้อยกเว้นดั้งเดิมใหม่และรีเซ็ตการติดตามสแต็กข้อยกเว้น
โยนอดีต รีเซ็ตการติดตามสแต็กข้อยกเว้นและรีเซ็ตประเภทข้อยกเว้น
ไม่มีคำตอบที่นี่แสดงความแตกต่างซึ่งอาจเป็นประโยชน์สำหรับคนที่พยายามเข้าใจความแตกต่าง พิจารณารหัสตัวอย่างนี้:
using System;
using System.Collections.Generic;
namespace ExceptionDemo
{
class Program
{
static void Main(string[] args)
{
void fail()
{
(null as string).Trim();
}
void bareThrow()
{
try
{
fail();
}
catch (Exception e)
{
throw;
}
}
void rethrow()
{
try
{
fail();
}
catch (Exception e)
{
throw e;
}
}
void innerThrow()
{
try
{
fail();
}
catch (Exception e)
{
throw new Exception("outer", e);
}
}
var cases = new Dictionary<string, Action>()
{
{ "Bare Throw:", bareThrow },
{ "Rethrow", rethrow },
{ "Inner Throw", innerThrow }
};
foreach (var c in cases)
{
Console.WriteLine(c.Key);
Console.WriteLine(new string('-', 40));
try
{
c.Value();
} catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
}
ซึ่งสร้างผลลัพธ์ต่อไปนี้:
Bare Throw:
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
at ExceptionDemo.Program.<>c.<Main>g__bareThrow|0_1() in C:\...\ExceptionDemo\Program.cs:line 19
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Rethrow
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<>c.<Main>g__rethrow|0_2() in C:\...\ExceptionDemo\Program.cs:line 35
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Inner Throw
----------------------------------------
System.Exception: outer ---> System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 43
--- End of inner exception stack trace ---
at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 47
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
การโยนเปล่าตามที่ระบุไว้ในคำตอบก่อนหน้านี้แสดงให้เห็นอย่างชัดเจนทั้งบรรทัดต้นฉบับของรหัสที่ล้มเหลว (บรรทัดที่ 12) รวมถึงจุดอีกสองจุดที่ใช้งานในการโทรสแต็กเมื่อเกิดข้อยกเว้น (บรรทัดที่ 19 และ 64)
ผลลัพธ์ของเคสตัวใหม่จะแสดงสาเหตุที่เป็นปัญหา เมื่อมีการสร้างข้อยกเว้นซ้ำเช่นนี้ข้อยกเว้นจะไม่รวมข้อมูลสแต็กดั้งเดิม โปรดทราบว่าจะรวมเฉพาะจุดthrow e(สาย 35) และจุดสแต็กการโทรด้านนอกสุด (บรรทัด 64) มันจะเป็นการยากที่จะติดตามเมธอด fail () เป็นแหล่งที่มาของปัญหาหากคุณโยนข้อยกเว้นด้วยวิธีนี้
กรณีสุดท้าย (InnerThrow) มีความละเอียดมากที่สุดและรวมถึงข้อมูลมากกว่าด้านใดด้านหนึ่ง เนื่องจากเรากำลังยกข้อยกเว้นใหม่ขึ้นมาเราจึงมีโอกาสเพิ่มข้อมูลบริบท (ข้อความ "outer" ที่นี่ แต่เรายังสามารถเพิ่มพจนานุกรม. Data ในข้อยกเว้นใหม่) รวมถึงการรักษาข้อมูลทั้งหมดในต้นฉบับ ข้อยกเว้น (รวมถึงลิงก์ช่วยเหลือพจนานุกรมข้อมูล ฯลฯ )