จับข้อยกเว้นหลายรายการพร้อมกันหรือไม่


2140

System.Exceptionมันเป็นกำลังใจที่จะเพียงแค่จับ ควรจับเฉพาะข้อยกเว้น "รู้จัก" เท่านั้น

ตอนนี้บางครั้งสิ่งนี้นำไปสู่รหัสซ้ำซ้อนที่ไม่จำเป็นเช่น:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

ฉันสงสัยว่า: มีวิธีจับทั้งข้อยกเว้นและโทรWebId = Guid.Emptyออกครั้งเดียวเท่านั้นหรือไม่

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


5
หากคุณใช้. net 4 ขึ้นไปฉันต้องการใช้ aggregateexception msdn.microsoft.com/en-us/library/system.aggregateexception.aspx
Bepenfriends

2
Bepenfriends- เนื่องจากSystem.Guidไม่ได้โยนAggregateExceptionมันจะดีมากถ้าคุณ (หรือบางคน) สามารถโพสต์คำตอบที่แสดงว่าคุณจะห่อมันลงใน AggregateException ฯลฯ ได้อย่างไร
weir


11
"ไม่แนะนำให้เพียงแค่จับ System.Exception" - และถ้าวิธีการนี้สามารถโยนข้อยกเว้นได้ 32 ประเภทจะมีวิธีใดบ้าง เขียนจับสำหรับแต่ละของพวกเขาแยกจากกัน?
giorgim

5
หากวิธีการของการขว้างข้อยกเว้น 32 ประเภทแตกต่างกันแสดงว่ามันเขียนไม่ดี ไม่ว่าจะเป็นข้อยกเว้นที่การโทรของตัวเองกำลังทำอยู่มันกำลังทำ FAR มากเกินไปในวิธีเดียวหรือ 32 ส่วนใหญ่ / ทั้งหมดนั้นควรเป็นข้อยกเว้นเพียงครั้งเดียวพร้อมรหัสเหตุผล
Flynn1179

คำตอบ:


2100

จับSystem.Exceptionและสลับกับชนิด

catch (Exception ex)            
{                
    if (ex is FormatException || ex is OverflowException)
    {
        WebId = Guid.Empty;
        return;
    }

    throw;
}

69
น่าเสียดายที่ FxCop (เช่น - การวิเคราะห์รหัส Visual Studio) ไม่ชอบเมื่อคุณพบข้อยกเว้น
Andrew Garrison

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

28
FxCop รุ่นล่าสุดไม่ส่งข้อยกเว้นเมื่อใช้รหัสข้างต้น
ปีเตอร์

28
ไม่แน่ใจว่าเกิดอะไรขึ้นกับรหัสของ OP ในตอนแรก คำตอบที่ได้รับการยอมรับอันดับ 1 นั้นมีเกือบสองเท่าของหลายบรรทัดและอ่านได้น้อยกว่ามาก
JoãoBragança

22
@ JoãoBragança: ในขณะที่คำตอบนี้ในตัวอย่างนี้ใช้บรรทัดมากขึ้นลองจินตนาการว่าคุณกำลังจัดการกับไฟล์ IO ตัวอย่างและสิ่งที่คุณต้องการทำคือจับข้อยกเว้นเหล่านั้นและส่งข้อความบันทึก แต่เฉพาะที่คุณคาดหวังมาจากคุณ ไฟล์วิธีการ IO จากนั้นคุณมักจะต้องจัดการกับข้อยกเว้นประเภทต่าง ๆ จำนวนมากขึ้น (ประมาณ 5 หรือมากกว่า) ในสถานการณ์เช่นนั้นวิธีการนี้สามารถช่วยคุณได้บ้าง
Xilconic

594

แก้ไข:ฉันเห็นด้วยกับคนอื่น ๆ ที่พูดว่าในฐานะของ C # 6.0 ตอนนี้ตัวกรองข้อยกเว้นเป็นวิธีที่ดีที่สุดที่จะไป:catch (Exception ex) when (ex is ... || ex is ... )

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

catch (Exception ex) when (
    ex is ...
    || ex is ...
    || ex is ...
)

ORIGINAL:

ฉันรู้ว่าฉันมาช้าไปงานปาร์ตี้ที่นี่ แต่ควันศักดิ์สิทธิ์ ...

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

private void TestMethod ()
{
    Action<Exception> errorHandler = ( ex ) => {
        // write to a log, whatever...
    };

    try
    {
        // try some stuff
    }
    catch ( FormatException  ex ) { errorHandler ( ex ); }
    catch ( OverflowException ex ) { errorHandler ( ex ); }
    catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}

ฉันอดไม่ได้ที่จะสงสัย ( คำเตือน:ประชดเล็ก ๆ น้อย ๆ / การประชดประชันข้างหน้า) ทำไมบนโลกถึงความพยายามทั้งหมดนี้โดยทั่วไปเพียงแค่แทนที่สิ่งต่อไปนี้:

try
{
    // try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

... ด้วยความแปรปรวนที่บ้าคลั่งของกลิ่นรหัสต่อไปนี้ฉันหมายถึงตัวอย่างเพียงแกล้งทำเป็นว่าคุณกำลังบันทึกการกดแป้นบางครั้ง

// sorta sucks, let's be honest...
try
{
    // try some stuff
}
catch( Exception ex )
{
    if (ex is FormatException ||
        ex is OverflowException ||
        ex is ArgumentNullException)
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

เพราะมันไม่สามารถอ่านได้โดยอัตโนมัติ

จริงอยู่ฉันออกจากอินสแตนซ์ที่เหมือนกันสามรายการ/* write to a log, whatever... */ return;จากตัวอย่างแรก

แต่นั่นเป็นจุดของฉัน คุณเคยได้ยินฟังก์ชั่น / วิธีการใช่มั้ย อย่างจริงจัง. เขียนErrorHandlerฟังก์ชั่นทั่วไปและเช่นเรียกมันจากแต่ละ catch block

หากคุณถามฉันตัวอย่างที่สอง (พร้อมกับifและisคำหลัก) มีทั้งที่อ่านได้น้อยลงและมีข้อผิดพลาดเกิดขึ้นได้ง่ายกว่าในระหว่างขั้นตอนการบำรุงรักษาของโครงการของคุณ

ขั้นตอนการบำรุงรักษาสำหรับผู้ที่อาจจะค่อนข้างใหม่ในการเขียนโปรแกรมจะประกอบด้วย 98.7% หรือมากกว่าของอายุการใช้งานโดยรวมของโครงการของคุณและ schmuck ที่ไม่ดีในการบำรุงรักษาเกือบจะแน่นอนเป็นคนอื่นที่ไม่ใช่คุณ และมีโอกาสที่ดีมากที่พวกเขาจะใช้เวลา 50% ในการสาปแช่งชื่อของคุณ

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

การวางการifทดสอบทั้งหมดในบรรทัดเดียวจะทำให้อ่านง่ายขึ้นหรือไม่ ฉันไม่คิดอย่างนั้น ฉันหมายความว่าฉันมีโปรแกรมเมอร์คนอื่นเถียงอีกครั้งเมื่อนานมาแล้วว่าการวางโค้ดเพิ่มเติมในบรรทัดเดียวจะทำให้ "ทำงานได้เร็วขึ้น" แต่แน่นอนว่าเขาเป็นคนบ้าคลั่ง พยายามอธิบายให้เขา (ด้วยหน้าตรง - ซึ่งท้าทาย) วิธีล่ามหรือคอมไพเลอร์จะแยกบรรทัดยาวนั้นออกเป็นคำสั่งหนึ่งคำสั่งต่อบรรทัดแยกกันโดยสิ้นเชิงเหมือนกับผลลัพธ์ถ้าเขาไปข้างหน้าและ เพิ่งทำให้โค้ดอ่านได้แทนที่จะพยายามคอมไพเลอร์ที่ฉลาด - ไม่มีผลอะไรกับเขาเลย แต่ฉันเชือนแช

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

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

แค่พูด...

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

36
เมื่อฉันสะดุดคำถามนี้ครั้งแรกฉันตอบคำถามได้ดี เจ๋งฉันสามารถExceptionตรวจสอบประเภททั้งหมดและตรวจสอบได้ ฉันคิดว่ามันล้างรหัส แต่สิ่งที่ทำให้ฉันกลับมาที่คำถามและฉันอ่านคำตอบของคำถาม ฉันเคี้ยวมันซักพักแล้ว แต่ฉันต้องเห็นด้วยกับคุณ มันเป็นมากขึ้นอ่านและการบำรุงรักษาที่จะใช้ฟังก์ชั่นให้แห้งขึ้นรหัสของคุณมากกว่าที่จะจับทุกอย่างตรวจสอบประเภทเมื่อเปรียบเทียบกับรายชื่อรหัสการตัดและการขว้างปา ขอบคุณที่มาช้าและให้ทางเลือกและสติ (IMO) +1
ข้อผิดพลาด

8
throw;การใช้ฟังก์ชั่นการจัดการข้อผิดพลาดจะไม่ทำงานหากคุณต้องการที่จะรวมถึง คุณจะต้องทำซ้ำบรรทัดของรหัสนั้นในแต่ละ catch block (เห็นได้ชัดว่าไม่ใช่จุดจบของโลก แต่ควรพูดถึงเนื่องจากมันเป็นรหัสที่จะต้องทำซ้ำ)
kad81

5
@ kad81 นั้นเป็นความจริง แต่คุณยังคงได้รับประโยชน์จากการเขียนบันทึกและทำความสะอาดรหัสในที่เดียวและเปลี่ยนมันในที่เดียวถ้าจำเป็นโดยไม่ต้องมีความหมายโง่ ๆ ของการจับประเภทการยกเว้นฐานแล้วแยกสาขาตาม ประเภทข้อยกเว้น และthrow();คำสั่งพิเศษหนึ่งคำสั่งในแต่ละ catch catch เป็นราคาเล็ก ๆ ที่ต้องจ่าย IMO และยังทำให้คุณอยู่ในสถานะที่จะทำการล้างข้อมูลเฉพาะประเภทยกเว้นถ้าจำเป็น
Craig

2
สวัสดี @Reitffunk เพียงแค่ใช้แทนFunc<Exception, MyEnumType> Action<Exception>นั่นคือFunc<T, Result>มีResultเป็นชนิดการส่งคืน
Craig

3
ฉันอยู่ในข้อตกลงที่สมบูรณ์ที่นี่ ฉันก็อ่านคำตอบแรกและความคิดก็ดูสมเหตุสมผล ย้ายไปที่หมายเลข 1 ทั่วไปสำหรับตัวจัดการข้อยกเว้นทั้งหมด บางสิ่งในตัวฉันทำให้ฉันอ้วกภายใน ... ดังนั้นฉันจึงเปลี่ยนรหัสกลับคืน จากนั้นก็พบกับความงามนี้! นี่ต้องเป็นคำตอบที่ได้รับการยอมรับ
Conor Gallagher

372

ตามที่คนอื่น ๆ ได้ชี้ให้เห็นคุณสามารถมีifคำสั่งในบล็อก catch ของคุณเพื่อกำหนดสิ่งที่เกิดขึ้น C # 6 รองรับตัวกรองข้อยกเว้นดังนั้นสิ่งต่อไปนี้จะใช้ได้:

try {  }
catch (Exception e) when (MyFilter(e))
{
    
}

MyFilterวิธีก็จะมีลักษณะบางอย่างเช่นนี้

private bool MyFilter(Exception e)
{
  return e is ArgumentNullException || e is FormatException;
}

อีกวิธีหนึ่งสามารถทำแบบอินไลน์ได้ (ทางด้านขวามือของคำสั่ง when ต้องเป็นนิพจน์บูลีน)

try {  }
catch (Exception e) when (e is ArgumentNullException || e is FormatException)
{
    
}

สิ่งนี้แตกต่างจากการใช้ifคำสั่งจากภายในcatchบล็อกการใช้ตัวกรองข้อยกเว้นจะไม่คลายกองซ้อน

คุณสามารถดาวน์โหลดVisual Studio 2015เพื่อลองดู

หากคุณต้องการใช้ Visual Studio 2013 ต่อไปคุณสามารถติดตั้งแพ็คเกจ nuget ต่อไปนี้:

ติดตั้งแพ็คเกจ Microsoft.Net.Compilers

ในขณะที่เขียนนี้จะรวมถึงการสนับสนุนสำหรับ C # 6

การอ้างอิงแพ็คเกจนี้จะทำให้โครงการสร้างขึ้นโดยใช้คอมไพเลอร์รุ่น C # และ Visual Basic เฉพาะที่อยู่ในแพ็คเกจซึ่งต่างจากรุ่นที่ติดตั้งระบบใด ๆ


3
อดทนรอการเปิดตัวอย่างเป็นทางการของ 6 ... ฉันต้องการที่จะเห็นสิ่งนี้ได้รับการตรวจสอบเมื่อเกิดขึ้น
RubberDuck

@RubberDuck ฉันกำลังจะตายสำหรับโอเปอเรเตอร์การแพร่กระจายของโมฆะจาก C # 6 การพยายามโน้มน้าวให้คนอื่น ๆ ในทีมของฉันเชื่อว่าความเสี่ยงของภาษา / คอมไพเลอร์จะไม่คุ้มค่า การปรับปรุงเล็กน้อยจำนวนมากที่มีผลกระทบอย่างมาก สำหรับการทำเครื่องหมายว่าเป็นคำตอบไม่สำคัญตราบใดที่ผู้คนตระหนักถึงความประสงค์ / เป็นไปได้นี้ฉันก็มีความสุข
โจ

ขวา?! ฉันจะดูฐานรหัสของฉันในอนาคตอันใกล้ =) ฉันรู้ว่าเช็คไม่สำคัญ แต่เมื่อได้รับคำตอบที่ยอมรับแล้วจะล้าสมัยฉันหวังว่า OP จะกลับมาตรวจสอบอีกครั้งเพื่อให้ทัศนวิสัยเหมาะสม
RubberDuck

นั่นเป็นเพียงบางส่วนที่ฉันยังไม่ได้รับรางวัล @Joe ฉันต้องการให้สิ่งนี้มองเห็นได้ คุณอาจต้องการเพิ่มตัวอย่างของตัวกรองอินไลน์เพื่อความชัดเจน
RubberDuck

188

ไม่ได้อยู่ใน C # เนื่องจากคุณต้องการตัวกรองข้อยกเว้นในการทำเช่นนั้นและ C # จะไม่เปิดเผยคุณสมบัติของ MSIL VB.NET มีความสามารถนี้เช่น

Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException

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

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}

26
ความคิดที่น่าสนใจและอีกตัวอย่างหนึ่งที่ VB.net มีข้อดีที่น่าสนใจบางกว่า C # บางครั้ง
ไมเคิล Stum

47
@MichaelStum กับที่ชนิดของไวยากรณ์ฉันแทบจะเรียกว่ามันน่าสนใจเลย ... สั่น
MarioDS

17
ตัวกรองข้อยกเว้นมาใน c # 6! สังเกตเห็นความแตกต่างของการใช้ฟิลเตอร์เพื่อการ rethrowing roslyn.codeplex.com/discussions/541301
Arne Deruwe

@ArneDeruwe ขอบคุณสำหรับลิงค์นี้! ฉันเพิ่งเรียนรู้เหตุผลสำคัญอีกข้อหนึ่งที่จะไม่โยนอีกต่อไป: throw e;ทำลาย stacktrace และ callstack throw;ทำลายcallstack "เท่านั้น" (เรนเดอร์ผิดพลาดทิ้งไร้ประโยชน์!) เหตุผลที่ดีมากที่จะใช้ไม่ว่าจะหลีกเลี่ยงได้!
AnorZaken

1
มีตัวกรองข้อยกเว้น C # 6 ให้บริการ! ในที่สุด
Danny

134

เพื่อความครบถ้วนสมบูรณ์ตั้งแต่. NET 4.0รหัสสามารถเขียนใหม่เป็น:

Guid.TryParse(queryString["web"], out WebId);

TryParseไม่เคยโยนข้อยกเว้นและผลตอบแทนที่เป็นเท็จถ้ารูปแบบไม่ถูกต้อง, การตั้งค่า WebId Guid.Emptyไป


ตั้งแต่C # 7คุณสามารถหลีกเลี่ยงการแนะนำตัวแปรในบรรทัดแยก:

Guid.TryParse(queryString["web"], out Guid webId);

คุณยังสามารถสร้างวิธีการแยกวิเคราะห์ tuples ที่ส่งคืนซึ่งไม่พร้อมใช้งานใน. NET Framework แต่เป็นรุ่น 4.6:

(bool success, Guid result) TryParseGuid(string input) =>
    (Guid.TryParse(input, out Guid result), result);

และใช้พวกเขาเช่นนี้

WebId = TryParseGuid(queryString["web"]).result;
// or
var tuple = TryParseGuid(queryString["web"]);
WebId = tuple.success ? tuple.result : DefaultWebId;

การอัปเดตที่ไร้ประโยชน์ครั้งต่อไปสำหรับคำตอบที่ไร้ประโยชน์นี้เกิดขึ้นเมื่อมีการสร้างโครงสร้างของพารามิเตอร์ออกใน C # 12 :)


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

9
ฉันรู้ว่าคุณหมายถึงอะไร แต่แน่นอนGuid.TryParseไม่กลับGuid.Emptyมา ถ้า string อยู่ในรูปแบบที่ไม่ถูกต้องก็ตั้งค่าresultพารามิเตอร์ที่ส่งออกไปGuid.Emptyแต่มันก็ส่งกลับ falseฉันกล่าวขวัญมันเพราะผมเคยเห็นรหัสที่ไม่สิ่งที่อยู่ในรูปแบบของGuid.TryParse(s, out guid); if (guid == Guid.Empty) { /* handle invalid s */ }ซึ่งมักจะผิดถ้าจะเป็นตัวแทนสตริงของs Guid.Empty

14
ว้าวคุณได้ตอบคำถามยกเว้นว่ามันไม่ได้อยู่ในจิตวิญญาณของคำถาม ปัญหาที่ใหญ่กว่าคืออย่างอื่น :(
nawfal

6
รูปแบบที่เหมาะสมสำหรับการใช้ TryParse แน่นอนเป็นมากกว่าif( Guid.TryParse(s, out guid){ /* success! */ } else { /* handle invalid s */ }ซึ่งไม่มีความคลุมเครือเหมือนตัวอย่างที่ใช้งานไม่ได้ซึ่งค่าอินพุตอาจเป็นตัวแทนสตริงของ Guid
Craig

2
คำตอบนี้อาจถูกต้องเกี่ยวกับ Guid.Parse แต่มันพลาดจุดทั้งหมดของคำถามเดิม ซึ่งไม่มีส่วนเกี่ยวข้องกับ Guid.Parse แต่เป็นเรื่องเกี่ยวกับการจับข้อยกเว้น vs FormatException / OverflowException / etc
Conor Gallagher

115

ขณะนี้มีตัวกรองข้อยกเว้นอยู่ใน c # 6+ คุณทำได้

try
{
       WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when(ex is FormatException || ex is OverflowException)
{
     WebId = Guid.Empty;
}

ใน C # 7.0+ คุณสามารถรวมสิ่งนี้กับการจับคู่รูปแบบได้เช่นกัน

try
{
   await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae &&
                           ae.InnerExceptions.Count > tasks.Count/2)
{
   //More than half of the tasks failed maybe..? 
}

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

74

หากคุณสามารถอัพเกรดแอปพลิเคชันของคุณเป็น C # 6 คุณจะโชคดี เวอร์ชัน C # ใหม่ได้ใช้ตัวกรองข้อยกเว้น ดังนั้นคุณสามารถเขียนสิ่งนี้:

catch (Exception ex) when (ex is FormatException || ex is OverflowException) {
    WebId = Guid.Empty;
}

บางคนคิดว่ารหัสนี้เหมือนกัน

catch (Exception ex) {                
    if (ex is FormatException || ex is OverflowException) {
        WebId = Guid.Empty;
    }
    throw;
}

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

ดูการอภิปรายเกี่ยวกับเรื่องนี้ใน CodePlex และตัวอย่างที่แสดงความแตกต่าง


4
การโยนโดยไม่มีข้อยกเว้นจะป้องกันสแต็ก แต่ "throw ex" จะเขียนทับมัน
อีวาน

32

หากคุณไม่ต้องการที่จะใช้ifคำสั่งภายในcatchขอบเขตในC# 6.0คุณสามารถใช้Exception Filtersไวยากรณ์ซึ่งได้รับการสนับสนุนแล้วโดย CLR ในรุ่นตัวอย่าง แต่เพียงลมปากVB.NET/ MSIL:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (Exception exception) when (exception is FormatException || ex is OverflowException)
{
    WebId = Guid.Empty;
}

รหัสนี้จะจับExceptionเฉพาะเมื่อมันเป็นInvalidDataExceptionArgumentNullExceptionหรือ

ที่จริงแล้วคุณสามารถใส่เงื่อนไขใด ๆ ไว้ในwhenข้อนั้นได้:

static int a = 8;

...

catch (Exception exception) when (exception is InvalidDataException && a == 8)
{
    Console.WriteLine("Catch");
}

โปรดทราบว่าเมื่อเทียบกับifคำสั่งที่อยู่ในcatchขอบเขตของException Filtersไม่สามารถโยนExceptionsและเมื่อพวกเขาทำหรือเมื่อไม่มีเงื่อนไขเงื่อนไขtrueต่อไปcatchจะถูกประเมินแทน:

static int a = 7;

static int b = 0;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

เอาท์พุท: จับทั่วไป

เมื่อมีมากกว่าหนึ่งtrue Exception Filter- คนแรกจะได้รับการยอมรับ:

static int a = 8;

static int b = 4;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

ผลลัพธ์: จับ

และอย่างที่คุณเห็นในMSILรหัสไม่ได้แปลเป็นifคำสั่ง แต่ถึงFiltersและExceptionsไม่สามารถโยนจากภายในพื้นที่ที่มีเครื่องหมายFilter 1และFilter 2แต่ตัวกรองการขว้างExceptionจะล้มเหลวแทนนอกจากนี้ค่าการเปรียบเทียบล่าสุดที่ผลักไปยังสแต็กก่อนendfilterคำสั่ง จะกำหนดความสำเร็จ / ความล้มเหลวของตัวกรอง ( Catch 1 XOR Catch 2จะทำงานตามลำดับ):

ตัวกรองข้อยกเว้น MSIL

นอกจากนี้ยังGuidมีGuid.TryParseวิธีการเฉพาะ


+1 สำหรับแสดงหลายรายการเมื่อตัวกรองและให้คำอธิบายว่าเกิดอะไรขึ้นเมื่อใช้ตัวกรองหลายรายการ
steven87vt

26

ด้วย C # 7 คำตอบจาก Michael Stumสามารถปรับปรุงได้ในขณะที่ยังคงความสามารถในการอ่านของ switch statement:

catch (Exception ex)
{
    switch (ex)
    {
        case FormatException _:
        case OverflowException _:
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

และด้วย C # 8 เป็นนิพจน์สวิตช์:

catch (Exception ex)
{
    WebId = ex switch
    {
        _ when ex is FormatException || ex is OverflowException => Guid.Empty,
        _ => throw ex
    };
}

3
นี่ควรเป็นคำตอบที่ได้รับการยอมรับในปี 2018 IMHO
MemphiZ

6
คำตอบของ Mat J ที่ใช้whenนั้นงดงามกว่า / เหมาะสมกว่าสวิตช์
rgoliveira

@rgoliveira: ฉันยอมรับว่าสำหรับกรณีที่ถามในคำถามคำตอบโดย Mat J นั้นจะดูดีและเหมาะสมกว่า อย่างไรก็ตามมันจะยากที่จะอ่านหากคุณมีรหัสที่แตกต่างกันที่คุณต้องการดำเนินการขึ้นอยู่กับประเภทของข้อยกเว้นหรือถ้าคุณต้องการใช้อินสแตนซ์ของข้อยกเว้น สถานการณ์ทั้งหมดเหล่านี้สามารถปฏิบัติได้อย่างเท่าเทียมกันด้วยคำสั่งสวิตช์นี้
เฟเบียน

1
@Fabian "หากคุณมีรหัสอื่นที่คุณต้องการใช้งานขึ้นอยู่กับประเภทของข้อยกเว้นหรือถ้าคุณต้องการใช้อินสแตนซ์ของข้อยกเว้น" จากนั้นคุณเพียงสร้างcatchบล็อกอื่นหรือคุณต้องส่งมันต่อไป .. จากประสบการณ์ของฉันthrow;ในcatchบล็อกของคุณน่าจะเป็นกลิ่นรหัส
rgoliveira

@rgoliveira: การใช้โยนในการจับบล็อกตกลงในหลายกรณีที่เห็นการเชื่อมโยง เนื่องจากคำสั่ง case ใช้ลิงก์การจับคู่รูปแบบจริง ๆคุณไม่จำเป็นต้องแปลงหากคุณแทนที่ลิงก์ตัวดำเนินการทิ้ง(ขีดล่าง) ด้วยชื่อตัวแปร อย่าเข้าใจฉันผิดฉันเห็นด้วยกับคุณว่าตัวกรองข้อยกเว้นเป็นวิธีที่สะอาดกว่าในการทำ แต่บล็อก catch หลายบล็อกเพิ่มวงเล็บปีกกาจำนวนมาก
เฟเบียน

20

คำตอบที่ยอมรับนั้นดูเหมือนยอมรับได้ยกเว้นว่า CodeAnalysis / FxCopจะบ่นเกี่ยวกับความจริงที่ว่ามันเป็นประเภทยกเว้นทั่วไป

นอกจากนี้ดูเหมือนว่าตัวดำเนินการ "เป็น" อาจลดประสิทธิภาพลงเล็กน้อย

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

อย่างไรก็ตามนี่คือสิ่งที่ฉันจะทำ:

bool exThrown = false;

try
{
    // Something
}
catch (FormatException) {
    exThrown = true;
}
catch (OverflowException) {
    exThrown = true;
}

if (exThrown)
{
    // Something else
}

19
แต่โปรดทราบว่าคุณไม่สามารถสร้างข้อยกเว้นขึ้นมาใหม่ได้โดยไม่สูญเสียการติดตามสแต็กหากคุณทำเช่นนี้ (ดูความคิดเห็นของ Michael Stum ต่อคำตอบที่ยอมรับ)
René

2
รูปแบบนี้สามารถปรับปรุงได้โดยการจัดเก็บข้อยกเว้น (โปรดแก้ตัวการจัดรูปแบบไม่ดี - ฉันไม่สามารถหาวิธีใส่รหัสในความคิดเห็น): Exception ex = null; ลอง {// บางสิ่ง} จับ (FormatException e) {ex = e; } catch (OverflowException e) {ex = e; } if (ex! = null) {// อย่างอื่นและจัดการกับ ex}
Jesse Weigert

3
@JesseWeigert: 1. คุณสามารถใช้ backticks เพื่อให้ชิ้นส่วนของข้อความเป็นแบบอักษรขาวดำและพื้นหลังสีเทาอ่อน 2. คุณยังคงไม่สามารถสร้างข้อยกเว้นเดิมได้รวมถึงสแต็กสแต็
โอลิเวอร์

2
@CleverNeologism แม้ว่ามันจะเป็นจริงที่การใช้isโอเปอเรเตอร์อาจมีผลกระทบด้านลบเล็กน้อยต่อประสิทธิภาพการทำงาน แต่ก็เป็นความจริงที่ว่าตัวจัดการข้อยกเว้นไม่ใช่สถานที่ที่จะต้องกังวลเกี่ยวกับการเพิ่มประสิทธิภาพ หากแอปของคุณใช้เวลาเป็นอย่างมากในตัวจัดการข้อยกเว้นซึ่งการเพิ่มประสิทธิภาพจะทำให้ความแตกต่างในประสิทธิภาพของแอปนั้นแตกต่างจากเดิมอย่างสิ้นเชิง ต้องบอกว่าฉันยังคงไม่ชอบวิธีแก้ปัญหานี้เพราะคุณสูญเสียการติดตามสแต็กและเนื่องจากการล้างข้อมูลจะถูกลบตามบริบทออกจากคำสั่ง catch
Craig

3
ครั้งเดียวที่isผู้ปฏิบัติงานลดประสิทธิภาพการทำงานคือถ้าคุณทำการดำเนินการในภายหลังas(ดังนั้นพวกเขาจึงมีคุณสมบัติตามกฎโดยไม่จำเป็น ) หากสิ่งที่คุณกำลังทำคือการทดสอบนักแสดงโดยไม่จำเป็นต้องทำการส่งนักแสดงisผู้ดำเนินการนั้นเป็นสิ่งที่คุณต้องการใช้
saluce

19

ใน C # 6 แนวทางที่แนะนำคือการใช้ตัวกรองข้อยกเว้นนี่คือตัวอย่าง:

 try
 {
      throw new OverflowException();
 }
 catch(Exception e ) when ((e is DivideByZeroException) || (e is OverflowException))
 {
       // this will execute iff e is DividedByZeroEx or OverflowEx
       Console.WriteLine("E");
 }

18

นี่เป็นคำตอบที่แตกต่างจากคำตอบของ Matt (ฉันรู้สึกว่ามันค่อนข้างสะอาดกว่า) ... ใช้วิธีการ:

public void TryCatch(...)
{
    try
    {
       // something
       return;
    }
    catch (FormatException) {}
    catch (OverflowException) {}

    WebId = Guid.Empty;
}

ข้อยกเว้นอื่น ๆ จะถูกโยนและรหัสWebId = Guid.Empty;จะไม่ถูกตี หากคุณไม่ต้องการให้ข้อยกเว้นอื่น ๆ ขัดข้องโปรแกรมของคุณเพียงเพิ่มสิ่งนี้หลังจากการจับอีกสองอัน:

...
catch (Exception)
{
     // something, if anything
     return; // only need this if you follow the example I gave and put it all in a method
}

-1 จะดำเนินการWebId = Guid.Emtpyในกรณีที่ไม่มีข้อยกเว้น
Sepster

4
@sepster ฉันคิดว่าคำสั่ง return หลังจาก "// some" เป็นนัยที่นี่ ฉันไม่ชอบวิธีแก้ปัญหา แต่นี่เป็นตัวแปรที่สร้างสรรค์ในการอภิปราย +1 เพื่อเลิกทำการ downvote ของคุณ :-)
toong

@Songster toong ถูกต้องฉันสันนิษฐานว่าถ้าคุณต้องการผลตอบแทนที่นั่นคุณจะต้องใส่ ... ฉันพยายามที่จะทำให้คำตอบของฉันทั่วไปพอที่จะนำไปใช้กับทุกสถานการณ์ในกรณีที่คนอื่นที่มีคำถามคล้ายกัน ดี. อย่างไรก็ตามสำหรับการวัดที่ดีฉันได้เพิ่มreturnคำตอบของฉัน ขอบคุณสำหรับการป้อนข้อมูล
bsara

18

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

catch(Exception ex)
{   
    if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

มีข้อดีบางประการในการย้อนกลับนิพจน์:

  • ไม่จำเป็นต้องมีข้อความสั่งคืนสินค้า
  • รหัสไม่ซ้อนกัน
  • ไม่มีความเสี่ยงที่จะลืมคำว่า 'การโยน' หรือ 'การกลับมา' ที่อยู่ในคำตอบของโจเซฟที่แยกออกจากการแสดงออก

มันยังสามารถอัดเป็นบรรทัดเดียว (แม้ว่าจะไม่สวยมาก)

catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

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

นี่คือปัญหาที่เกิดขึ้นโดยใช้ไวยากรณ์ C # 6.0:

catch(Exception ex) when (ex is SomeException || ex is OtherException)
{
    // Handle exception
}

2
+1 นี่คือคำตอบที่ดีที่สุด มันดีกว่าคำตอบยอดนิยมส่วนใหญ่เพราะไม่มีreturnแม้ว่าการกลับสภาพจะดีขึ้นเล็กน้อย
DCShannon

ฉันไม่ได้คิดอย่างนั้น เยี่ยมมากฉันจะเพิ่มเข้าไปในรายการ
Stefan T

16

@Micheal

รหัสของคุณมีการแก้ไขเล็กน้อย:

catch (Exception ex)
{
   Type exType = ex.GetType();
   if (exType == typeof(System.FormatException) || 
       exType == typeof(System.OverflowException)
   {
       WebId = Guid.Empty;
   } else {
      throw;
   }
}

การเปรียบเทียบสตริงน่าเกลียดและช้า


21
ทำไมไม่เพียงใช้คำหลัก "เป็น"?
Chris Pietschmann

29
@Michael - หาก Microsoft แนะนำให้ใช้ StringTooLongException ที่ได้มาจาก FormatException ก็ยังคงเป็นข้อยกเว้นการจัดรูปแบบเพียงอันเดียว ขึ้นอยู่กับว่าคุณต้องการซีแมนทิกส์ของ 'catch catch exception exception ประเภท' หรือ 'catch catch exceptions ที่หมายความว่ารูปแบบของสตริงนั้นผิด'
Greg Beech

6
@Michael - นอกจากนี้โปรดทราบว่า "catch (FormatException ex) มีความหมายหลังมันจะตรวจจับสิ่งใดก็ตามที่ได้จาก FormatException
Greg Beech

14
@ เลขที่อเล็กซ์ "ส่ง" โดยไม่มี "อดีต" มีข้อยกเว้นดั้งเดิมรวมถึงการติดตามสแต็กดั้งเดิมขึ้น การเพิ่ม "ex" ทำให้การติดตามสแต็กมีการรีเซ็ตดังนั้นคุณจะได้รับข้อยกเว้นที่แตกต่างจากของจริง ฉันแน่ใจว่าคนอื่นสามารถอธิบายได้ดีกว่าฉัน :)
Samantha Branham

13
-1: รหัสนี้มีความเปราะบางอย่างยิ่งนักพัฒนาห้องสมุดคาดว่าจะแทนที่throw new FormatException();ด้วยthrow new NewlyDerivedFromFormatException();โดยไม่ทำลายโค้ดโดยใช้ไลบรารีและจะถือเป็นจริงสำหรับกรณีการจัดการข้อยกเว้นทั้งหมดยกเว้นกรณีที่มีคนใช้==แทนis(หรือเพียงอย่างเดียวcatch (FormatException))
Sam Harwell

13

เกี่ยวกับ

try
{
    WebId = Guid.Empty;
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}

ใช้งานได้หาก Catch-Code สามารถย้ายเข้าไปใน Try-Block ได้อย่างสมบูรณ์ แต่โค้ดอิมเมจที่คุณทำการปรับแต่งหลายอย่างให้กับวัตถุและอีกอันหนึ่งอยู่ตรงกลางล้มเหลวและคุณต้องการ "รีเซ็ต" วัตถุ
Michael Stum

4
ในกรณีนั้นฉันจะเพิ่มฟังก์ชั่นการรีเซ็ตและเรียกมันว่าจากบล็อก catch หลายอัน
Maurice


11

ข้อควรระวังและคำเตือน: ยังเป็นอีกรูปแบบที่ใช้งานได้

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

static void Main() 
{ 
    Action body = () => { ...your code... };

    body.Catch<InvalidOperationException>() 
        .Catch<BadCodeException>() 
        .Catch<AnotherException>(ex => { ...handler... })(); 
}

(โดยทั่วไปให้อื่นที่ว่างเปล่า Catchโอเวอร์โหลดที่ซึ่งจะส่งคืนตัวเอง)

คำถามที่ใหญ่กว่านี้คือเหตุผลที่ ฉันไม่คิดว่าค่าใช้จ่ายจะสูงกว่ากำไรที่นี่ :)


1
ข้อดีอย่างหนึ่งที่เป็นไปได้ของวิธีนี้คือมีความแตกต่างทางความหมายระหว่างการจับและการสร้างใหม่ข้อยกเว้นเมื่อเทียบกับการไม่จับมัน ในบางกรณีรหัสควรปฏิบัติตามข้อยกเว้นโดยไม่ต้องจับมัน สิ่งนี้เป็นไปได้ใน vb.net แต่ไม่ได้อยู่ใน C # ยกเว้นกรณีที่ใช้ wrapper ที่เขียนใน vb.net และเรียกจาก C #
supercat

1
จะดำเนินการกับข้อยกเว้นโดยไม่จับได้อย่างไร ฉันไม่เข้าใจคุณอย่างเต็มที่
nawfal

@nawful ... โดยใช้ตัวกรอง vb - ฟังก์ชัน filt (ยกเว้น ex): LogEx (ex): ส่งคืน false ... จากนั้นใน catch line: catch ex เมื่อ filt (ex)
FastAl

1
@FastAl ไม่ใช่ตัวกรองข้อยกเว้นอะไรใน C # 6
HimBromBeere

@HimBromBe ที่นี่พวกเขากำลัง analogues โดยตรง
FastAl

9

อัปเดต 2015-12-15: ดูhttps://stackoverflow.com/a/22864936/1718702สำหรับ C # 6 มันสะอาดและตอนนี้เป็นมาตรฐานในภาษา

เหมาะสำหรับผู้ที่ต้องการทางออกที่หรูหรากว่าเพื่อจับครั้งเดียวและกรองข้อยกเว้นฉันใช้วิธีการขยายดังที่แสดงด้านล่าง

ฉันมีส่วนขยายนี้ในห้องสมุดของฉันซึ่งเดิมเขียนขึ้นเพื่อจุดประสงค์อื่น ๆ แต่มันก็ทำงานได้อย่างสมบูรณ์แบบสำหรับการtypeตรวจสอบข้อยกเว้น ยิ่งไปกว่านั้นมันดูสะอาดกว่า||คำพูดมากมาย นอกจากนี้ยังแตกต่างจากคำตอบที่ยอมรับฉันชอบการจัดการข้อยกเว้นที่ชัดเจนดังนั้นจึงex is ...มีพฤติกรรมที่ไม่พึงประสงค์เนื่องจากคลาสที่ได้รับมานั้นสามารถกำหนดให้กับประเภทพาเรนต์ได้)

การใช้

if (ex.GetType().IsAnyOf(
    typeof(FormatException),
    typeof(ArgumentException)))
{
    // Handle
}
else
    throw;

IsAnyOf.cs Extension (ดูตัวอย่างการจัดการข้อผิดพลาดแบบเต็มสำหรับการพึ่งพา)

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }
    }
}

ตัวอย่างการจัดการข้อผิดพลาดแบบเต็ม (คัดลอกวางไปยังแอปคอนโซลใหม่)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.FluentValidation;

namespace IsAnyOfExceptionHandlerSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // High Level Error Handler (Log and Crash App)
            try
            {
                Foo();
            }
            catch (OutOfMemoryException ex)
            {
                Console.WriteLine("FATAL ERROR! System Crashing. " + ex.Message);
                Console.ReadKey();
            }
        }

        static void Foo()
        {
            // Init
            List<Action<string>> TestActions = new List<Action<string>>()
            {
                (key) => { throw new FormatException(); },
                (key) => { throw new ArgumentException(); },
                (key) => { throw new KeyNotFoundException();},
                (key) => { throw new OutOfMemoryException(); },
            };

            // Run
            foreach (var FooAction in TestActions)
            {
                // Mid-Level Error Handler (Appends Data for Log)
                try
                {
                    // Init
                    var SomeKeyPassedToFoo = "FooParam";

                    // Low-Level Handler (Handle/Log and Keep going)
                    try
                    {
                        FooAction(SomeKeyPassedToFoo);
                    }
                    catch (Exception ex)
                    {
                        if (ex.GetType().IsAnyOf(
                            typeof(FormatException),
                            typeof(ArgumentException)))
                        {
                            // Handle
                            Console.WriteLine("ex was {0}", ex.GetType().Name);
                            Console.ReadKey();
                        }
                        else
                        {
                            // Add some Debug info
                            ex.Data.Add("SomeKeyPassedToFoo", SomeKeyPassedToFoo.ToString());
                            throw;
                        }
                    }
                }
                catch (KeyNotFoundException ex)
                {
                    // Handle differently
                    Console.WriteLine(ex.Message);

                    int Count = 0;
                    if (!Validate.IsAnyNull(ex, ex.Data, ex.Data.Keys))
                        foreach (var Key in ex.Data.Keys)
                            Console.WriteLine(
                                "[{0}][\"{1}\" = {2}]",
                                Count, Key, ex.Data[Key]);

                    Console.ReadKey();
                }
            }
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }

        /// <summary>
        /// Validates if any passed in parameter is equal to null.
        /// </summary>
        /// <param name="p_parameters">Parameters to test for Null.</param>
        /// <returns>True if one or more parameters are null.</returns>
        public static bool IsAnyNull(params object[] p_parameters)
        {
            p_parameters
                .CannotBeNullOrEmpty("p_parameters");

            foreach (var item in p_parameters)
                if (item == null)
                    return true;

            return false;
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        public static void CannotBeNull(this object p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw
                    new
                        ArgumentNullException(
                        string.Format("Parameter \"{0}\" cannot be null.",
                        p_name), default(Exception));
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static void CannotBeNullOrEmpty<T>(this ICollection<T> p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw new ArgumentNullException("Collection cannot be null.\r\nParameter_Name: " + p_name, default(Exception));

            if (p_parameter.Count <= 0)
                throw new ArgumentOutOfRangeException("Collection cannot be empty.\r\nParameter_Name: " + p_name, default(Exception));
        }

        /// <summary>
        /// Validates the passed in parameter is not null or empty, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentException"></exception>
        public static void CannotBeNullOrEmpty(this string p_parameter, string p_name)
        {
            if (string.IsNullOrEmpty(p_parameter))
                throw new ArgumentException("String cannot be null or empty.\r\nParameter_Name: " + p_name, default(Exception));
        }
    }
}

ตัวอย่างการทดสอบหน่วย NUnit สองตัวอย่าง

พฤติกรรมการจับคู่สำหรับExceptionประเภทนั้นแน่นอน (เช่นเด็กไม่ตรงกับประเภทหลักใด ๆ )

using System;
using System.Collections.Generic;
using Common.FluentValidation;
using NUnit.Framework;

namespace UnitTests.Common.Fluent_Validations
{
    [TestFixture]
    public class IsAnyOf_Tests
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void IsAnyOf_ArgumentNullException_ShouldNotMatch_ArgumentException_Test()
        {
            Action TestMethod = () => { throw new ArgumentNullException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(ArgumentException), /*Note: ArgumentNullException derrived from ArgumentException*/
                    typeof(FormatException),
                    typeof(KeyNotFoundException)))
                {
                    // Handle expected Exceptions
                    return;
                }

                //else throw original
                throw;
            }
        }

        [Test, ExpectedException(typeof(OutOfMemoryException))]
        public void IsAnyOf_OutOfMemoryException_ShouldMatch_OutOfMemoryException_Test()
        {
            Action TestMethod = () => { throw new OutOfMemoryException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(OutOfMemoryException),
                    typeof(StackOverflowException)))
                    throw;

                /*else... Handle other exception types, typically by logging to file*/
            }
        }
    }
}

1
การเสริมภาษาไม่ได้ "สง่างาม" ในหลาย ๆ ที่สิ่งนี้ได้สร้างนรกแห่งการบำรุงรักษา หลายปีต่อมาโปรแกรมเมอร์หลายคนไม่ภูมิใจในสิ่งที่พวกเขาสร้าง ไม่ใช่สิ่งที่คุณคุ้นเคยในการอ่าน มันอาจทำให้ "huh?" เอฟเฟกต์หรือ "WTFs" ที่รุนแรง บางครั้งมันก็สับสน สิ่งเดียวที่ทำให้รหัสนั้นยากกว่าสำหรับผู้ที่ต้องการจัดการกับมันในภายหลังในการบำรุงรักษา - เพียงเพราะโปรแกรมเมอร์คนหนึ่งพยายามที่จะ "ฉลาด" ในช่วงหลายปีที่ผ่านมาฉันได้เรียนรู้ว่าคำตอบที่ "ฉลาด" เหล่านั้นเป็นวิธีที่ดีเช่นกัน
Kaii

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

นอกจากนี้ยังทราบวิธีการแก้ปัญหาของคุณเท่านั้นที่ใกล้เคียงกับความหมายของ C # 6 whenราวกับจะใด ๆcatch (Exception ex) {if (...) {/*handle*/} throw;}รุ่นของ มูลค่าที่แท้จริงของwhenคือการที่ตัวกรองทำงานก่อนที่จะถูกจับได้ดังนั้นจึงหลีกเลี่ยงความเสียหายจากค่าใช้จ่าย / สแต็คของการโยนซ้ำ มันใช้ประโยชน์จากคุณสมบัติ CLR ที่ก่อนหน้านี้เข้าถึงได้กับ VB และ MSIL เท่านั้น
Marc L.

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

IsAnyOfวิธีการทั้งหมดของคุณสามารถเขียนใหม่ได้อย่างง่ายดายp_comparisons.Contains(p_parameter)
maksymiuk

7

เนื่องจากฉันรู้สึกว่าคำตอบเหล่านี้เพิ่งสัมผัสพื้นผิวฉันจึงพยายามขุดให้ลึกขึ้น

ดังนั้นสิ่งที่เราต้องการทำจริงๆคือสิ่งที่ไม่ได้รวบรวมพูดว่า:

// Won't compile... damn
public static void Main()
{
    try
    {
        throw new ArgumentOutOfRangeException();
    }
    catch (ArgumentOutOfRangeException)
    catch (IndexOutOfRangeException) 
    {
        // ... handle
    }

เหตุผลที่เราต้องการสิ่งนี้เพราะเราไม่ต้องการให้ตัวจัดการข้อยกเว้นจับสิ่งที่เราต้องการในภายหลังในกระบวนการ แน่นอนว่าเราสามารถจับข้อยกเว้นและตรวจสอบด้วย 'ถ้า' จะทำอย่างไร แต่ขอซื่อสัตย์เราไม่ต้องการมัน (FxCop, ปัญหาการดีบัก, ความอัปลักษณ์)

ดังนั้นทำไมรหัสนี้จึงไม่คอมไพล์ - และเราจะแฮ็คมันได้อย่างไรในแบบที่มันจะเป็น?

หากเราดูรหัสสิ่งที่เราอยากทำจริงๆคือส่งต่อสาย อย่างไรก็ตามตาม MS Partition II บล็อกตัวจัดการข้อยกเว้นของ IL จะไม่ทำงานเช่นนี้ซึ่งในกรณีนี้เหมาะสมเพราะนั่นหมายความว่าวัตถุ 'ข้อยกเว้น' อาจมีประเภทที่แตกต่างกัน

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

// Won't compile... damn
try
{
    throw new ArgumentOutOfRangeException();
}
catch (ArgumentOutOfRangeException e) {
    goto theOtherHandler;
}
catch (IndexOutOfRangeException e) {
theOtherHandler:
    Console.WriteLine("Handle!");
}

สาเหตุที่การรวบรวมนี้ไม่ชัดเจนนัก: ชนิดและค่าใดที่ออบเจ็กต์ '$ exception' มี (ซึ่งเก็บไว้ที่นี่ในตัวแปร 'e') วิธีที่เราต้องการให้คอมไพเลอร์จัดการกับสิ่งนี้คือการสังเกตว่าประเภทพื้นฐานทั่วไปของข้อยกเว้นทั้งสองคือ 'ข้อยกเว้น' ใช้สำหรับตัวแปรเพื่อให้มีข้อยกเว้นทั้งสองและจัดการข้อยกเว้นสองข้อที่จับได้เท่านั้น วิธีนี้นำมาใช้ใน IL เป็น 'ตัวกรอง' ซึ่งมีอยู่ใน VB.Net

เพื่อให้ทำงานใน C # เราต้องการตัวแปรชั่วคราวที่มีประเภทฐาน 'ข้อยกเว้น' ที่ถูกต้อง เพื่อควบคุมการไหลของรหัสเราสามารถเพิ่มบางสาขา ไปที่นี่:

    Exception ex;
    try
    {
        throw new ArgumentException(); // for demo purposes; won't be caught.
        goto noCatch;
    }
    catch (ArgumentOutOfRangeException e) {
        ex = e;
    }
    catch (IndexOutOfRangeException e) {
        ex = e;
    }

    Console.WriteLine("Handle the exception 'ex' here :-)");
    // throw ex ?

noCatch:
    Console.WriteLine("We're done with the exception handling.");

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

Exception ex = null;
try
{
    throw new ArgumentException();
}
catch (ArgumentOutOfRangeException e)
{
    ex = e;
}
catch (IndexOutOfRangeException e)
{
    ex = e;
}
if (ex != null)
{
    Console.WriteLine("Handle the exception here :-)");
}

นั่นเป็นเพียง 'โยนใหม่' เพื่อให้ทำงานได้เราต้องสามารถจัดการภายในบล็อก 'catch' และวิธีเดียวที่จะทำให้งานนี้ได้โดยการจับ 'ยกเว้น' วัตถุ

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

private static bool Handle(Exception e)
{
    Console.WriteLine("Handle the exception here :-)");
    return true; // false will re-throw;
}

public static void Main()
{
    try
    {
        throw new OutOfMemoryException();
    }
    catch (ArgumentException e)
    {
        if (!Handle(e)) { throw; }
    }
    catch (IndexOutOfRangeException e)
    {
        if (!Handle(e)) { throw; }
    }

    Console.WriteLine("We're done with the exception handling.");

อีกวิธีคือจับวัตถุยกเว้นและจัดการตามนั้น การแปลที่แท้จริงมากที่สุดสำหรับสิ่งนี้ตามบริบทข้างต้นคือ:

try
{
    throw new ArgumentException();
}
catch (Exception e)
{
    Exception ex = (Exception)(e as ArgumentException) ?? (e as IndexOutOfRangeException);
    if (ex != null)
    {
        Console.WriteLine("Handle the exception here :-)");
        // throw ?
    }
    else 
    {
        throw;
    }
}

ดังนั้นเพื่อสรุป:

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

7

นี่เป็นปัญหาคลาสสิกที่นักพัฒนา C # ทุกคนต้องเผชิญในที่สุด

ให้ฉันแบ่งคำถามของคุณออกเป็น 2 คำถาม ครั้งแรก

ฉันสามารถตรวจจับข้อยกเว้นหลายรายการพร้อมกันได้หรือไม่?

ในระยะสั้นไม่มี

ซึ่งนำไปสู่คำถามต่อไป

ฉันจะหลีกเลี่ยงการเขียนรหัสซ้ำได้อย่างไรเนื่องจากฉันไม่สามารถตรวจพบข้อยกเว้นหลายประเภทในบล็อก catch () เดียวกันได้

จากตัวอย่างเฉพาะของคุณซึ่งค่าการถอยกลับมีราคาถูกในการสร้างฉันชอบทำตามขั้นตอนเหล่านี้:

  1. เตรียมข้อมูลเบื้องต้นของ WebId เป็นค่าตัวเลือกการถอยกลับ
  2. สร้าง Guid ใหม่ในตัวแปรชั่วคราว
  3. ตั้งค่า WebId เป็นตัวแปรชั่วคราวที่สร้างขึ้นอย่างสมบูรณ์ ทำให้นี่เป็นคำสั่งสุดท้ายของบล็อก try {}

ดังนั้นรหัสดูเหมือนว่า:

try
{
    WebId = Guid.Empty;
    Guid newGuid = new Guid(queryString["web"]);
    // More initialization code goes here like 
    // newGuid.x = y;
    WebId = newGuid;
}
catch (FormatException) {}
catch (OverflowException) {}

หากมีข้อผิดพลาดเกิดขึ้น WebId จะไม่ถูกตั้งค่าเป็นค่าที่สร้างไว้ครึ่งและยังคง Guid.Empty

หากการสร้างค่า fall-back มีราคาแพงและการรีเซ็ตค่านั้นถูกกว่ามากฉันจะย้ายรหัสรีเซ็ตไปยังฟังก์ชันของตัวเอง:

try
{
    WebId = new Guid(queryString["web"]);
    // More initialization code goes here.
}
catch (FormatException) {
    Reset(WebId);
}
catch (OverflowException) {
    Reset(WebId);
}

นี่เป็นสิ่งที่ดี "การเข้ารหัสเชิงนิเวศน์" คือคุณกำลังคิดล่วงหน้าเกี่ยวกับรหัสและข้อมูลของคุณ ดีใจที่ได้ติดตามแบบนี้ขอบคุณ Jeffrey!
Tahir Khalid

6

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

ดังนั้นรหัสของคุณลงมาถึงสิ่งนี้:

MyClass instance;
try { instance = ... }
catch(Exception1 e) { Reset(instance); }
catch(Exception2 e) { Reset(instance); }
catch(Exception) { throw; }

void Reset(MyClass instance) { /* reset the state of the instance */ }

ฉันสงสัยว่าทำไมไม่มีใครสังเกตเห็นว่าการทำสำเนารหัส

จาก C # 6 นอกจากนี้คุณยังมีตัวกรองข้อยกเว้นตามที่ผู้อื่นกล่าวถึงแล้ว ดังนั้นคุณสามารถแก้ไขโค้ดด้านบนนี้:

try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{ 
    Reset(instance); 
}

3
"ฉันสงสัยว่าทำไมไม่มีใครสังเกตเห็นการทำสำเนารหัสนั้น" - เอ๊ะอะไรนะ? จุดทั้งหมดของคำถามคือการกำจัดความซ้ำซ้อนรหัส
Mark Amery

4

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

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

            try
            {
                // do some work here
            }
            catch (WebException ex)
            {
                // catch a web excpetion
            }
            catch (ArgumentException ex)
            {
                // do some stuff
            }
            catch (Exception ex)
            {
                // you should really surface your errors but this is for example only
                throw new Exception("An error occurred: " + ex.Message);
            }

ฉันขอแนะนำให้คนอ่านเอกสาร MSDN นี้:

ลำดับชั้นข้อยกเว้น


4

อาจพยายามทำให้รหัสของคุณง่ายขึ้นเช่นวางรหัสทั่วไปในวิธีการเช่นเดียวกับที่คุณทำในส่วนอื่น ๆ ของรหัสที่ไม่ได้อยู่ในประโยคจับ?

เช่น:

try
{
    // ...
}
catch (FormatException)
{
    DoSomething();
}
catch (OverflowException)
{
    DoSomething();
}

// ...

private void DoSomething()
{
    // ...
}

ฉันจะทำอย่างไรการพยายามหาสิ่งเรียบง่ายเป็นลวดลายที่สวยงาม


3

โปรดทราบว่าฉันพบวิธีที่จะทำ แต่ดูเหมือนว่าเนื้อหาสำหรับThe Daily WTF :

catch (Exception ex)
{
    switch (ex.GetType().Name)
    {
        case "System.FormatException":
        case "System.OverflowException":
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

9
-1 โหวต, +5 WTF :-) สิ่งนี้ไม่ควรถูกทำเครื่องหมายว่าเป็นคำตอบ แต่เป็นคนที่น่ากลัว
แอรอน

1
ไม่สำคัญว่าเราจะทำได้ง่ายๆ แต่เขาไม่ได้นั่งเฉยๆและคิดหาวิธีแก้ ขอบคุณจริงๆ
Maxymus

2
อย่าทำสิ่งนี้จริง ๆ ใช้ตัวกรองข้อยกเว้นใน C # 6 หรือคำตอบอื่น ๆ - ฉันวางสิ่งนี้ไว้ที่นี่โดยเฉพาะว่า "นี่เป็นวิธีหนึ่ง แต่ไม่ดีและฉันต้องการทำสิ่งที่ดีกว่า"
Michael Stum

ทำไมถึงไม่ดี? ฉันงงคุณไม่สามารถใช้ข้อยกเว้นในคำสั่ง switch ได้โดยตรง
MKesper

3
@ MKesper ฉันเห็นเหตุผลสองสามอย่างที่มันไม่ดี มันต้องมีการเขียนชื่อคลาสที่ผ่านการรับรองเป็นสตริงตัวอักษรซึ่งเสี่ยงต่อการพิมพ์ผิดที่คอมไพเลอร์ไม่สามารถบันทึกคุณได้ (สิ่งนี้มีความสำคัญเนื่องจากในกรณีที่มีข้อผิดพลาดในร้านค้าหลายแห่งมีการทดสอบน้อยและมีข้อผิดพลาดเล็กน้อยในนั้นมีแนวโน้มที่จะพลาดได้) นอกจากนี้ยังจะไม่ตรงกับข้อยกเว้นซึ่งเป็นคลาสย่อยของหนึ่งในกรณีที่ระบุ และเนื่องจากเป็นสตริงกรณีต่างๆจะไม่ได้รับความสนใจจากเครื่องมืออย่าง VS "ค้นหาการอ้างอิงทั้งหมด" - หากคุณต้องการเพิ่มขั้นตอนการล้างข้อมูลในทุกที่ที่มีข้อยกเว้นเกิดขึ้น
Mark Amery

2

เป็นมูลค่าการกล่าวขวัญที่นี่ คุณสามารถตอบสนองต่อชุดค่าผสมหลายชุด (ข้อผิดพลาดข้อยกเว้นและ exception.message)

ฉันวิ่งเข้าไปในสถานการณ์กรณีใช้งานเมื่อพยายามที่จะโยนวัตถุควบคุมใน datagrid โดยมีเนื้อหาเป็น TextBox, TextBlock หรือ CheckBox ในกรณีนี้ Exception ที่ส่งคืนจะเหมือนกัน แต่ข้อความนั้นเปลี่ยนแปลง

try
{
 //do something
}
catch (Exception ex) when (ex.Message.Equals("the_error_message1_here"))
{
//do whatever you like
} 
catch (Exception ex) when (ex.Message.Equals("the_error_message2_here"))
{
//do whatever you like
} 

0

ฉันอยากจะแนะนำคำตอบที่สั้นที่สุด ( สไตล์การทำงานมากกว่าหนึ่ง)

        Catch<FormatException, OverflowException>(() =>
            {
                WebId = new Guid(queryString["web"]);
            },
            exception =>
            {
                WebId = Guid.Empty;
            });

สำหรับสิ่งนี้คุณต้องสร้างการโอเวอร์โหลดแบบ "Catch" หลายวิธีคล้ายกับ System.Action:

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2));
    }

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2, TException3>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2), typeof(TException3));
    }

และอื่น ๆ ตามที่คุณต้องการ แต่คุณต้องทำครั้งเดียวและคุณสามารถใช้มันในทุกโครงการของคุณ (หรือถ้าคุณสร้างแพ็คเกจ nuget เราก็สามารถใช้มันได้เช่นกัน)

และการติดตั้ง CatchMany:

    [DebuggerNonUserCode]
    public static void CatchMany(Action tryBlock, Action<Exception> catchBlock,
        params Type[] exceptionTypes)
    {
        try
        {
            tryBlock();
        }
        catch (Exception exception)
        {
            if (exceptionTypes.Contains(exception.GetType())) catchBlock(exception);
            else throw;
        }
    }

ปล. ฉันไม่ได้ทำการตรวจสอบค่าความเรียบง่ายของโค้ดลองพิจารณาเพิ่มการตรวจสอบความถูกต้องของพารามิเตอร์

ps2 ถ้าคุณต้องการส่งคืนค่าจาก catch คุณจำเป็นต้องใช้วิธีการ Catch เดียวกัน แต่ใช้ return และ Func แทน Action ในพารามิเตอร์


-15

เพียงโทรลองและจับสองครั้ง

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
try
{
    WebId = new Guid(queryString["web"]);
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

มันเป็นเรื่องง่ายมาก !!


3
หนอ นี่คือการเอาชนะวัตถุประสงค์ของคำถาม เขาถามคำถามนี้เพื่อกำจัดรหัสที่ซ้ำกัน คำตอบนี้เพิ่มรหัสซ้ำกันมากขึ้น
James Esh

-23

ใน c # 6.0 ตัวกรองข้อยกเว้นเป็นการปรับปรุงสำหรับการจัดการข้อยกเว้น

try
{
    DoSomeHttpRequest();
}
catch (System.Web.HttpException e)
{
    switch (e.GetHttpCode())
    {
        case 400:
            WriteLine("Bad Request");
        case 500:
            WriteLine("Internal Server Error");
        default:
            WriteLine("Generic Error");
    }
}

13
ตัวอย่างนี้ไม่แสดงการใช้ตัวกรองข้อยกเว้นใด ๆ
user247702

นี่เป็นวิธีมาตรฐานในการกรองข้อยกเว้นใน c # 6.0
Kashif

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

6
ตัวอย่างของการกรองข้อยกเว้นคือcatch (HttpException e) when e.GetHttpCode() == 400 { WriteLine("Bad Request"; }
saluce
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.