แนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการข้อยกเว้นในแอปพลิเคชัน Windows Forms?


118

ฉันกำลังอยู่ในขั้นตอนการเขียนแอปพลิเคชัน Windows Forms ตัวแรก ตอนนี้ฉันอ่านหนังสือ C # สองสามเล่มแล้วดังนั้นฉันจึงมีความเข้าใจค่อนข้างดีว่าคุณลักษณะของภาษา C # มีข้อยกเว้นอะไรบ้าง พวกเขาทั้งหมดค่อนข้างเป็นทฤษฎีอย่างไรก็ตามสิ่งที่ฉันยังไม่ได้รับคือความรู้สึกในการแปลแนวคิดพื้นฐานเป็นรูปแบบการจัดการข้อยกเว้นที่ดีในแอปพลิเคชันของฉัน

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

สิ่งสำคัญที่ฉันกำลังพยายามหาคือ:

  • ฉันควรโยนข้อยกเว้นอีกครั้งเมื่อใด
  • ฉันควรลองมีกลไกจัดการข้อผิดพลาดส่วนกลางบ้างไหม
  • การจัดการข้อยกเว้นที่อาจถูกโยนทิ้งมีผลการดำเนินงานเมื่อเทียบกับการทดสอบล่วงหน้าอย่างไร้ค่าเช่นมีไฟล์ในดิสก์หรือไม่
  • โค้ดที่เรียกใช้งานได้ทั้งหมดควรอยู่ในบล็อก try-catch-last หรือไม่?
  • มีบางครั้งหรือไม่ที่อาจยอมรับบล็อกการจับที่ว่างได้?

รับคำแนะนำทั้งหมดอย่างสุดซึ้ง!

คำตอบ:


79

อีกไม่กี่บิต ...

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

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

อย่า "กลืน" ข้อยกเว้นยกเว้นในกรณีที่มีเอกสารดีที่สุดเมื่อคุณมั่นใจอย่างยิ่งว่าข้อยกเว้นที่ถูกโยนทิ้งนั้นน่าอยู่ นี้แทบจะไม่เคยเป็นเช่นนั้น (และถ้ามันเป็นให้แน่ใจว่าคุณกำลังกลืนกินเพียงเฉพาะระดับยกเว้น - ไม่เคยกลืนSystem.Exception.)

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

throw ex;

ในขณะที่คุณจะลบกลุ่มการโทร หากคุณต้องโยนซ้ำ (ซึ่งจำเป็นในบางครั้งเช่นเมื่อใช้ Exception Handling Block ของ Enterprise Library) ให้ใช้สิ่งต่อไปนี้:

throw;

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

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


1
หากปล่อยให้มีข้อยกเว้นเกิดขึ้นผู้โทรควรจะรู้ได้อย่างไรว่าข้อยกเว้นระบุว่าการดำเนินการล้มเหลว แต่โดยพื้นฐานแล้วระบบไม่เป็นไร (เช่นผู้ใช้พยายามเปิดไฟล์เอกสารที่เสียหายไฟล์ไม่โหลด แต่ทุกอย่าง อย่างอื่นก็น่าจะใช้ได้) หรือว่ามันบ่งชี้ว่า CPU ลุกเป็นไฟและควรรีบออกให้เร็วที่สุด? บางสิ่งบางอย่างเช่น ArgumentException อาจบ่งชี้อย่างใดอย่างหนึ่งขึ้นอยู่กับสถานการณ์ที่เกิด
supercat

2
@supercat โดยการเขียนคลาสย่อยเฉพาะApplicationExceptionสำหรับกรณีความล้มเหลวเหล่านั้นซึ่งแอปพลิเคชันควรจะแยกความแตกต่างและจัดการได้อย่างสมเหตุสมผล
Matt Enright

@ Matt Enright: แน่นอนว่าเป็นไปได้ที่จะจับข้อยกเว้นของตัวเองได้ แต่ฉันไม่รู้ว่ามีอะไรที่คล้ายกับอนุสัญญาจากระยะไกลซึ่งโมดูลที่มีข้อยกเว้นระบุว่าพวกเขาบ่งบอกถึงความเสียหายของรัฐใด ๆ ที่อยู่นอกเหนือจากความล้มเหลวโดยนัย ตามหลักการแล้ววิธีการเช่น SuperDocument.CreateFromFile () จะประสบความสำเร็จโยนข้อยกเว้น CleanFailure หรือโยน SomethingReallyBadHappenedException น่าเสียดายที่ถ้าไม่มีใครจะรวมทุกอย่างที่อาจทำให้เกิดข้อยกเว้นภายในบล็อกของตัวเองไม่มีทางรู้ได้ว่า InvalidOperationException หรือไม่ ...
supercat

@ Matt Enright: ... ควรห่อด้วย CleanFailureException หรือ SomethingReallyBadHappenedException และการรวมทุกอย่างไว้ในบล็อกลองจับแต่ละครั้งจะทำให้จุดประสงค์ทั้งหมดของการมีข้อยกเว้นตั้งแต่แรก
supercat

2
ฉันคิดว่าเป็นการออกแบบห้องสมุดที่ไม่ดีเพื่อซ่อนโหมดของความล้มเหลวด้วยประเภทข้อยกเว้นที่ไม่ถูกต้อง อย่าโยน FileNotFoundException หากสิ่งที่คุณหมายถึงคือ IOException หรือ InvalidDataException เนื่องจากแอปพลิเคชันต้องตอบสนองแตกต่างกันไปในแต่ละกรณี สิ่งต่างๆเช่น StackOverflowException หรือ OutOfMemoryException ไม่สามารถจัดการได้อย่างสมเหตุสมผลโดยแอปพลิเคชันดังนั้นเพียงแค่ปล่อยให้แอปพลิเคชันมีความประพฤติดีมีตัวจัดการ "ทางเลือกสุดท้าย" แบบรวมศูนย์อยู่แล้ว
Matt Enright

63

มีรหัสที่ยอดเยี่ยมเป็นCodeProject บทความที่นี่ ไฮไลท์สองสามข้อมีดังนี้

  • วางแผนให้แย่ที่สุด *
  • ตรวจสอบก่อน
  • อย่าเชื่อถือข้อมูลภายนอก
  • อุปกรณ์เดียวที่เชื่อถือได้ ได้แก่ วิดีโอเมาส์และคีย์บอร์ด
  • การเขียนสามารถล้มเหลวได้เช่นกัน
  • รหัสอย่างปลอดภัย
  • อย่าโยนข้อยกเว้นใหม่ ()
  • อย่าใส่ข้อมูลข้อยกเว้นที่สำคัญในช่องข้อความ
  • ใส่จุดเดียว (ข้อยกเว้น) ต่อเธรด
  • ควรเผยแพร่ข้อยกเว้นทั่วไปที่ตรวจพบ
  • บันทึกข้อยกเว้น ToString (); อย่าบันทึกเฉพาะข้อยกเว้นข้อความ!
  • อย่าจับ (ข้อยกเว้น) มากกว่าหนึ่งครั้งต่อเธรด
  • อย่ากลืนข้อยกเว้น
  • ควรใส่รหัสล้างข้อมูลในบล็อกสุดท้าย
  • ใช้ "ใช้" ทุกที่
  • อย่าส่งคืนค่าพิเศษในเงื่อนไขข้อผิดพลาด
  • อย่าใช้ข้อยกเว้นเพื่อระบุว่าไม่มีทรัพยากร
  • อย่าใช้การจัดการข้อยกเว้นเป็นวิธีการส่งคืนข้อมูลจากวิธีการ
  • ใช้ข้อยกเว้นสำหรับข้อผิดพลาดที่ไม่ควรละเลย
  • อย่าล้างการติดตามสแต็กเมื่อโยนข้อยกเว้นซ้ำ
  • หลีกเลี่ยงการเปลี่ยนแปลงข้อยกเว้นโดยไม่ต้องเพิ่มค่าความหมาย
  • ควรทำเครื่องหมายข้อยกเว้น [Serializable]
  • เมื่อมีข้อสงสัยอย่ายืนยันโยนข้อยกเว้น
  • ข้อยกเว้นแต่ละคลาสควรมีตัวสร้างดั้งเดิมอย่างน้อยสามตัว
  • โปรดใช้ความระมัดระวังเมื่อใช้เหตุการณ์ AppDomain.UnhandledException
  • อย่าสร้างล้อใหม่
  • อย่าใช้ Unstructured Error Handling (VB.Net)

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

ลิงก์ไปยังบทความที่อ้างอิงหมดอายุแล้ว รหัสโครงการแสดงให้เห็นว่าบทความนี้อาจจะได้ย้ายไปอยู่ที่นี่
DavidRR

15

โปรดทราบว่า Windows Forms มีกลไกการจัดการข้อยกเว้นของตัวเอง หากคลิกปุ่มในแบบฟอร์มและตัวจัดการแสดงข้อยกเว้นที่ไม่ได้ติดอยู่ในตัวจัดการ Windows Forms จะแสดงกล่องโต้ตอบข้อยกเว้นที่ไม่สามารถจัดการได้เอง

เพื่อป้องกันไม่ให้กล่องโต้ตอบข้อยกเว้นที่ไม่ได้จัดการแสดงขึ้นและตรวจจับข้อยกเว้นดังกล่าวสำหรับการบันทึกและ / หรือสำหรับการจัดเตรียมกล่องโต้ตอบข้อผิดพลาดของคุณเองคุณสามารถแนบกับเหตุการณ์ Application.ThreadException ก่อนการเรียกใช้ Application.Run () ในเมธอด Main () ของคุณ


ขอบคุณสำหรับคำแนะนำนี้ฉันได้เรียนรู้สิ่งนี้ช้าเกินไปฉันเพิ่งตรวจสอบใน linqpad ทำงานได้ตามที่คาดไว้ผู้รับมอบสิทธิ์คือ: โมฆะ Form1_UIThreadException (ผู้ส่งวัตถุ ThreadExceptionEventArgs t) แหล่งข้อมูลที่ดีอีกแหล่งหนึ่งเกี่ยวกับหัวข้อนี้คือ richnewman.wordpress.com/2007/ 04/08 / … สำหรับการจัดการข้อยกเว้นที่ไม่ได้จัดการโดยรวม: เหตุการณ์ AppDomain.UnhandledException มีไว้สำหรับข้อยกเว้นที่ไม่สามารถจัดการได้จากเธรดที่ไม่ใช่ UI หลัก
zhaorufei

14

คำแนะนำทั้งหมดที่โพสต์ไว้ที่นี่เป็นสิ่งที่ดีและควรค่าแก่การเอาใจใส่

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

หลักการง่ายๆคือ "บล็อกลอง / จับมีราคาแพง" นั่นไม่เป็นความจริง พยายามไม่แพง มันเป็นการจับที่ระบบต้องสร้างอ็อบเจ็กต์ Exception และโหลดขึ้นมาพร้อมกับ stack trace ซึ่งมีราคาแพง มีหลายกรณีที่มีข้อยกเว้นดีมากพอที่จะรวมโค้ดไว้ในบล็อก try / catch ได้อย่างสมบูรณ์แบบ

ตัวอย่างเช่นหากคุณกำลังเติมพจนานุกรมสิ่งนี้:

try
{
   dict.Add(key, value);
}
catch(KeyException)
{
}

มักจะเร็วกว่าการทำเช่นนี้:

if (!dict.ContainsKey(key))
{
   dict.Add(key, value);
}

สำหรับทุกรายการที่คุณกำลังเพิ่มเนื่องจากข้อยกเว้นจะเกิดขึ้นเมื่อคุณเพิ่มคีย์ที่ซ้ำกันเท่านั้น (LINQ แบบสอบถามรวมทำสิ่งนี้)

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

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


3
ในกรณีของ dict คุณสามารถทำได้: dict[key] = valueซึ่งควรจะเร็วที่สุดหากไม่เร็วกว่านั้น ..
nawfal

9

นี่คือหลักเกณฑ์บางประการที่ฉันปฏิบัติตาม

  1. Fail-Fast: นี่เป็นข้อยกเว้นในการสร้างแนวทางสำหรับทุกสมมติฐานที่คุณทำและทุกพารามิเตอร์ที่คุณเข้าสู่ฟังก์ชันจะตรวจสอบเพื่อให้แน่ใจว่าคุณเริ่มต้นด้วยข้อมูลที่ถูกต้องและสมมติฐานที่คุณตั้งไว้ กำลังทำถูกต้อง การตรวจสอบโดยทั่วไป ได้แก่ อาร์กิวเมนต์ไม่เป็นโมฆะอาร์กิวเมนต์ในช่วงที่คาดไว้เป็นต้น

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

  3. อย่าจับข้อยกเว้นที่คุณไม่สามารถจัดการได้ดังนั้นอย่ากังวลกับสิ่งต่างๆเช่น OutOfMemoryException เพราะหากเกิดขึ้นคุณจะไม่สามารถทำอะไรได้มากอีกต่อไป

  4. เชื่อมโยงตัวจัดการข้อยกเว้นส่วนกลางและตรวจสอบให้แน่ใจว่าได้บันทึกข้อมูลให้มากที่สุด สำหรับ winforms ขอให้ทั้งเหตุการณ์ข้อยกเว้นของ appdomain และเธรดที่ไม่สามารถจัดการได้

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

  6. มีหลายครั้งที่จำเป็นต้องใช้บล็อค Catch ที่ว่างเปล่าฉันคิดว่าคนที่พูดอย่างอื่นไม่ได้ทำงานกับโค้ดเบสที่มีการพัฒนามาหลายรุ่น แต่ควรได้รับการแสดงความคิดเห็นและตรวจสอบเพื่อให้แน่ใจว่าจำเป็นจริงๆ ตัวอย่างที่พบบ่อยที่สุดคือนักพัฒนาที่ใช้ try / catch เพื่อแปลงสตริงเป็นจำนวนเต็มแทนที่จะใช้ ParseInt ()

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


อะไรคือการใช้ในการเชื่อมต่อทั้ง appdomain และเธรดตัวจัดการข้อยกเว้นที่ไม่สามารถจัดการได้ หากคุณใช้ Appdomain.UnhandledException นั่นไม่ใช่สิ่งที่ทั่วไปที่สุดที่จะจับทุกอย่างได้หรือไม่?
Quagmire

คุณหมายถึงจัดการApplication.ThreadExceptionเหตุการณ์เมื่อคุณอ้างถึงเหตุการณ์ "เธรดที่ไม่จัดการข้อยกเว้น" ใช่หรือไม่
Jeff B

4

ฉันชอบปรัชญาของการไม่จับสิ่งใด ๆ ที่ฉันไม่ได้ตั้งใจจะจัดการไม่ว่าการจัดการจะหมายถึงอะไรในบริบทเฉพาะของฉัน

ฉันเกลียดมันเมื่อฉันเห็นรหัสเช่น:

try
{
   // some stuff is done here
}
catch
{
}

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

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

ฉันคิดว่าโค้ดควรเขียนในเชิงรุกและควรมีข้อยกเว้นสำหรับสถานการณ์พิเศษไม่ใช่เพื่อหลีกเลี่ยงการทดสอบเงื่อนไข


@Kiquenet ขออภัยฉันพูดไม่ชัดเจน สิ่งที่ฉันหมายถึง: อย่าทำการจับที่ว่างเปล่าเช่นนั้นให้เพิ่มตัวจัดการไปยังDispatcher แทน UnhandledExceptionและเพิ่มอย่างน้อยบันทึกเพื่อไม่ให้กินโดยไม่มีเกล็ดขนมปัง :) แต่ถ้าคุณไม่มีข้อกำหนดของส่วนประกอบที่เงียบคุณควรรวมข้อยกเว้นไว้ด้วย ในการออกแบบของคุณ โยนทิ้งเมื่อจำเป็นต้องโยนทำสัญญาที่ชัดเจนสำหรับคุณ Interfaces / API / Class ใด ๆ
LuckyLikey

4

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

  1. ตรวจสอบเงื่อนไขข้อผิดพลาดที่ทราบทั้งหมดอย่างชัดเจน *
  2. เพิ่ม try / catch รอบ ๆ โค้ดหากคุณไม่แน่ใจว่าคุณสามารถจัดการทุกกรณีได้หรือไม่
  3. เพิ่ม try / catch รอบ ๆ โค้ดหากอินเทอร์เฟซ. NET ที่คุณกำลังเรียกใช้มีข้อยกเว้น
  4. เพิ่ม try / catch รอบ ๆ โค้ดหากเกินเกณฑ์ความซับซ้อนสำหรับคุณ
  5. เพิ่มการลอง / จับรอบรหัสหากเพื่อการตรวจสอบความถูกต้อง: คุณยืนยันว่าสิ่งนี้ไม่ควรเกิดขึ้น
  6. ตามกฎทั่วไปฉันไม่ใช้ข้อยกเว้นแทนรหัสส่งคืน นี่ใช้ได้ดีสำหรับ. NET แต่ไม่ใช่ฉัน ฉันมีข้อยกเว้น (ฮิฮิ) สำหรับกฎนี้ แต่ขึ้นอยู่กับสถาปัตยกรรมของแอปพลิเคชันที่คุณใช้งานด้วย

*เนื่องด้วยเหตุผล. ไม่จำเป็นต้องตรวจสอบเพื่อดูว่ารังสีคอสมิกกระทบข้อมูลของคุณหรือไม่ซึ่งทำให้เกิดการพลิกสองสามบิต การทำความเข้าใจว่าอะไร "สมเหตุสมผล" เป็นทักษะที่ได้มาสำหรับวิศวกร ยากที่จะหาปริมาณ แต่ง่ายต่อการใช้งาน นั่นคือฉันสามารถอธิบายได้อย่างง่ายดายว่าเหตุใดฉันจึงใช้ try / catch ในกรณีใด ๆ แต่ฉันก็ยากที่จะปลูกฝังคนอื่นด้วยความรู้เดียวกันนี้

ฉันสำหรับคนหนึ่งมักจะหลีกเลี่ยงจากสถาปัตยกรรมที่มีข้อยกเว้นอย่างมาก try / catch ไม่มีผลการทำงานเช่นนี้การเข้าชมจะเกิดขึ้นเมื่อมีการโยนข้อยกเว้นและรหัสอาจต้องเดินขึ้นหลายระดับของ call stack ก่อนที่จะมีบางอย่างจัดการ


4

กฎทองที่พยายามยึดมั่นคือจัดการข้อยกเว้นให้ใกล้เคียงกับแหล่งที่มามากที่สุด

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

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

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


"กฎทอง" นี้เป็นไปตามอำเภอใจที่ดีที่สุด
Evan Harper

3

คุณสามารถดักจับเหตุการณ์ ThreadException

  1. เลือกโครงการ Windows Application ใน Solution Explorer

  2. เปิดไฟล์ Program.cs ที่สร้างขึ้นโดยดับเบิลคลิกที่ไฟล์นั้น

  3. เพิ่มบรรทัดโค้ดต่อไปนี้ที่ด้านบนของไฟล์โค้ด:

    using System.Threading;
  4. ในเมธอด Main () ให้เพิ่มสิ่งต่อไปนี้เป็นบรรทัดแรกของวิธีการ:

    Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
  5. เพิ่มสิ่งต่อไปนี้ด้านล่างเมธอด Main ():

    static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        // Do logging or whatever here
        Application.Exit();
    }
  6. เพิ่มรหัสเพื่อจัดการข้อยกเว้นที่ไม่สามารถจัดการได้ภายในตัวจัดการเหตุการณ์ ข้อยกเว้นใด ๆ ที่ไม่ได้รับการจัดการที่อื่นในแอปพลิเคชันจะได้รับการจัดการโดยรหัสด้านบน โดยทั่วไปรหัสนี้ควรบันทึกข้อผิดพลาดและแสดงข้อความถึงผู้ใช้

การอ้างอิง: https://blogs.msmvps.com/deborahk/global-exception-handler-winforms/


@Kiquenet ขอโทษฉันไม่รู้เกี่ยวกับ VB
Omid-RH

2

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

อย่าโยนซ้ำหากปล่อยให้ข้อยกเว้นเพิ่มขึ้นก็ทำได้เช่นกัน อย่าปล่อยให้ข้อผิดพลาดผ่านไปโดยไม่มีใครสังเกตเห็น

ตัวอย่าง:

void Main()
{
  try {
    DoStuff();
  }
  catch(Exception ex) {
    LogStuff(ex.ToString());
  }

void DoStuff() {
... Stuff ...
}

หาก DoStuff ผิดพลาดคุณจะต้องการให้ประกันตัว ข้อยกเว้นจะถูกโยนขึ้นไปที่ main และคุณจะเห็นรถไฟของเหตุการณ์ใน stack trace ของ ex


1

ฉันควรโยนข้อยกเว้นอีกครั้งเมื่อใด

ทุกที่ แต่วิธีการของผู้ใช้ ... เช่นตัวจัดการการคลิกปุ่ม

ฉันควรลองมีกลไกจัดการข้อผิดพลาดส่วนกลางบ้างไหม

ฉันเขียนไฟล์บันทึก ... ค่อนข้างง่ายสำหรับแอป WinForm

การจัดการข้อยกเว้นที่อาจถูกโยนทิ้งมีผลการดำเนินงานเมื่อเทียบกับการทดสอบล่วงหน้าอย่างไร้ค่าเช่นมีไฟล์ในดิสก์หรือไม่

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

โค้ดที่เรียกใช้งานได้ทั้งหมดควรอยู่ในบล็อก try-catch-last หรือไม่?

yeap

มีบางครั้งหรือไม่ที่อาจยอมรับบล็อกการจับที่ว่างได้?

ใช่สมมติว่าคุณต้องการแสดงวันที่ แต่คุณไม่รู้ว่าวันที่นั้นเป็นร้านค้าอย่างไร (dd / mm / yyyy, mm / dd / yyyy ฯลฯ ) คุณลองแยกวิเคราะห์ tp แต่ถ้าล้มเหลวให้ดำเนินการต่อ .. . ถ้ามันไม่เกี่ยวข้องกับคุณ ... ฉันจะตอบว่าใช่มี


1

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

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


1

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

Try
{
int a = 10 / 0;
}
catch(exception e){
//error logging
throw;
}

การทำเช่นนี้จะทำให้การติดตามสแต็กสิ้นสุดในคำสั่ง catch (หลีกเลี่ยงสิ่งนี้)

catch(Exception e)
// logging
throw e;
}

สิ่งนี้ยังใช้ได้กับ. NET Framework และ. NET Core เวอร์ชันล่าสุดหรือไม่
LuckyLikey

1

ประสบการณ์ของฉันฉันเห็นว่าเหมาะสมที่จะจับข้อยกเว้นเมื่อฉันรู้ว่าฉันจะสร้างมันขึ้นมา สำหรับอินสแตนซ์เมื่อฉันอยู่ในเว็บแอปพลิเคชันและฉันกำลังดำเนินการ Response.Redirect ฉันรู้ว่าฉันจะได้รับ System.ThreadAbortException เนื่องจากเป็นความตั้งใจฉันเพียงแค่จับเฉพาะประเภทและกลืนลงไป

try
{
/*Doing stuff that may cause an exception*/
Response.Redirect("http:\\www.somewhereelse.com");
}
catch (ThreadAbortException tex){/*Ignore*/}
catch (Exception ex){/*HandleException*/}

1

ฉันเห็นด้วยอย่างยิ่งกับกฎของ:

  • อย่าปล่อยให้ข้อผิดพลาดผ่านไปโดยไม่มีใครสังเกตเห็น

เหตุผลก็คือ:

  • เมื่อคุณจดรหัสครั้งแรกเป็นไปได้มากว่าคุณจะไม่มีความรู้ทั้งหมดเกี่ยวกับรหัสของบุคคลที่สามลีรารี่. NET FCL หรือการมีส่วนร่วมล่าสุดของเพื่อนร่วมงานของคุณ ในความเป็นจริงคุณไม่สามารถปฏิเสธที่จะเขียนโค้ดได้จนกว่าคุณจะทราบทุกความเป็นไปได้ของข้อยกเว้น ดังนั้น
  • ฉันพบว่าฉันใช้ try / catch (Exception ex) เพียงเพราะฉันต้องการป้องกันตัวเองจากสิ่งที่ไม่รู้จักและอย่างที่คุณสังเกตเห็นฉันจับ Exception ไม่ใช่เฉพาะเจาะจงมากขึ้นเช่น OutOfMemoryException เป็นต้นและฉันมักจะทำข้อยกเว้น ถูกโผล่ขึ้นมาให้ฉัน (หรือ QA) โดย ForceAssert AlwaysAssert (เท็จเช่น ToString ());

ForceAssertAlwaysAssert เป็นวิธีการ Trace ส่วนตัวของฉันยืนยันโดยไม่คำนึงว่ามาโคร DEBUG / TRACE จะถูกกำหนดหรือไม่

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

ด้วยวิธีนี้ฉันสามารถจดรหัสของฉันได้ในเวลาอันสั้นและปกป้องฉันจากโดเมนที่ไม่รู้จัก แต่จะสังเกตเห็นได้เสมอหากมีสิ่งผิดปกติเกิดขึ้นด้วยวิธีนี้ระบบจึงปลอดภัยและปลอดภัยมากขึ้น

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

สำหรับรหัส WinForms กฎทองที่ฉันปฏิบัติตามเสมอคือ:

  • พยายาม / จับ (ข้อยกเว้น) รหัสตัวจัดการเหตุการณ์ของคุณเสมอ

สิ่งนี้จะป้องกัน UI ของคุณให้ใช้งานได้ตลอดเวลา

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

ข้อยกเว้นควรเกิดขึ้นโดยมีโอกาสเพียงเล็กน้อยมิฉะนั้นก็ไม่ใช่ข้อยกเว้น


-1

คุณต้องคิดถึงผู้ใช้ แอปพลิเคชันขัดข้องเป็นครั้งสุดท้ายสิ่งที่ผู้ใช้ต้องการ ดังนั้นการดำเนินการใด ๆ ที่สามารถล้มเหลวควรมี try catch block ที่ระดับ ui ไม่จำเป็นต้องใช้ try catch ในทุกวิธี แต่ทุกครั้งที่ผู้ใช้ทำอะไรบางอย่างจะต้องสามารถจัดการข้อยกเว้นทั่วไปได้ นั่นไม่ได้ช่วยให้คุณตรวจสอบทุกอย่างเพื่อป้องกันข้อยกเว้นในกรณีแรกได้ แต่ไม่มีแอปพลิเคชันที่ซับซ้อนที่ไม่มีข้อบกพร่องและระบบปฏิบัติการสามารถเพิ่มปัญหาที่ไม่คาดคิดได้อย่างง่ายดายดังนั้นคุณต้องคาดการณ์สิ่งที่ไม่คาดคิดและตรวจสอบให้แน่ใจว่าผู้ใช้ต้องการใช้หรือไม่ การดำเนินการจะไม่มีข้อมูลสูญหายเนื่องจากแอปขัดข้อง ไม่จำเป็นต้องปล่อยให้แอปของคุณขัดข้องหากคุณพบข้อยกเว้นแอปจะไม่อยู่ในสถานะที่ไม่แน่นอนและผู้ใช้จะไม่ได้รับความสะดวกจากเหตุขัดข้องเสมอไป แม้ว่าข้อยกเว้นจะอยู่ในระดับสูงสุด การไม่ขัดข้องหมายความว่าผู้ใช้สามารถสร้างข้อยกเว้นซ้ำได้อย่างรวดเร็วหรืออย่างน้อยก็บันทึกข้อความแสดงข้อผิดพลาดดังนั้นจึงช่วยคุณแก้ไขปัญหาได้อย่างมาก แน่นอนว่าเป็นมากกว่าการรับข้อความแสดงข้อผิดพลาดธรรมดา ๆ แล้วเห็นเฉพาะหน้าต่างแสดงข้อผิดพลาดของ windows หรืออะไรทำนองนั้น

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

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


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