มีข้อยกเว้นอะไรบ้างที่ควรส่งออกมาสำหรับพารามิเตอร์ที่ไม่ถูกต้องหรือไม่คาดคิดใน.


163

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

ติดตาม:

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

คำตอบ:


249

ผมชอบที่จะใช้: ArgumentException, และArgumentNullExceptionArgumentOutOfRangeException

  • ArgumentException - มีบางอย่างผิดปกติกับการโต้แย้ง
  • ArgumentNullException - อาร์กิวเมนต์เป็นค่าว่าง
  • ArgumentOutOfRangeException - ฉันไม่ได้ใช้อันนี้มากนัก แต่การใช้งานทั่วไปคือการจัดทำดัชนีลงในคอลเลกชันและให้ดัชนีที่มีขนาดใหญ่

มีตัวเลือกอื่น ๆ เช่นกันที่ไม่ได้มุ่งเน้นไปที่การโต้แย้งมากนัก แต่ให้ตัดสินการโทรโดยรวม:

  • InvalidOperationException- อาร์กิวเมนต์อาจตกลง แต่ไม่อยู่ในสถานะปัจจุบันของวัตถุ เครดิตไปที่ STW (ก่อนหน้านี้ Yoooder) โหวตคำตอบของเขาด้วยเช่นกัน
  • NotSupportedException- ข้อโต้แย้งที่ส่งผ่านมีความถูกต้อง แต่ไม่รองรับในการใช้งาน ลองนึกภาพไคลเอนต์ FTP และคุณส่งคำสั่งโดยที่ไคลเอ็นต์ไม่รองรับ

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

ฉันชอบเมื่อข้อความแสดงข้อผิดพลาดชี้ไปที่วิธีใช้เอกสารหรือทรัพยากรอื่น ๆ ตัวอย่างเช่น Microsoft ทำได้ดีในขั้นแรกกับบทความ KB ของพวกเขาเช่น"ทำไมฉันได้รับข้อความแสดงข้อผิดพลาด" Operation aborted "เมื่อฉันเยี่ยมชมเว็บเพจใน Internet Explorer?" . เมื่อคุณพบข้อผิดพลาดพวกเขาจะนำคุณไปยังบทความ KB ในข้อความข้อผิดพลาด สิ่งที่พวกเขาทำได้ไม่ดีคือพวกเขาไม่บอกคุณทำไมมันล้มเหลวโดยเฉพาะ

ขอบคุณ STW (เช่น Yoooder) อีกครั้งสำหรับความคิดเห็น


ArgumentOutOfRangeExceptionในการตอบสนองต่อการติดตามของคุณฉันจะโยน ดูสิ่งที่ MSDN พูดเกี่ยวกับข้อยกเว้นนี้:

ArgumentOutOfRangeExceptionจะถูกโยนทิ้งเมื่อมีการเรียกใช้เมธอดและอาร์กิวเมนต์อย่างน้อยหนึ่งรายการที่ผ่านไปยังเมธอดนั้นไม่ใช่การอ้างอิงแบบ null ( Nothingใน Visual Basic) และไม่มีค่าที่ถูกต้อง

ดังนั้นในกรณีนี้คุณกำลังผ่านค่า แต่นั่นไม่ใช่ค่าที่ถูกต้องเนื่องจากช่วงของคุณคือ 1–12 อย่างไรก็ตามวิธีที่คุณจัดทำเอกสารชัดเจนว่า API ของคุณพ่นอะไร เพราะถึงแม้ว่าฉันอาจจะบอกว่านักพัฒนาอื่นอาจจะบอกว่าArgumentOutOfRangeException ArgumentExceptionทำให้ง่ายและบันทึกพฤติกรรม


Psst! ฉันยอมรับว่าคุณตอบคำถามเฉพาะของเขาอย่างถูกต้อง แต่ดูคำตอบของฉันด้านล่างเพื่อช่วยเขียนโค้ดการตรวจสอบและการตรวจสอบความถูกต้องของพารามิเตอร์ ;-D
STW

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

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

4
หมายเหตุถ้าคุณจับ ArgumentException ก็จะจับ ArgumentOutOfRange
Steven Evers

วิธีการเกี่ยวกับFormatException: ข้อยกเว้นที่เกิดขึ้นเมื่อรูปแบบของอาร์กิวเมนต์ไม่ถูกต้องหรือเมื่อรูปแบบสตริงแบบผสมไม่ได้เกิดขึ้น
Anthony

44

ฉันโหวตให้คำตอบของ Joshแต่ต้องการเพิ่มอีกหนึ่งรายการ:

ควรส่งออก System.InvalidOperationException หากอาร์กิวเมนต์นั้นถูกต้อง แต่วัตถุอยู่ในสถานะที่ไม่ควรใช้อาร์กิวเมนต์

อัปเดตจาก MSDN:

InvalidOperationException จะใช้ในกรณีที่ความล้มเหลวในการเรียกใช้วิธีการที่เกิดจากเหตุผลอื่นที่ไม่ใช่ข้อโต้แย้งที่ไม่ถูกต้อง

สมมติว่าวัตถุของคุณมีเมธอด PerformAction (enmSomeAction) enmSomeActions ที่ใช้ได้คือ Open และ Close ถ้าคุณเรียก PerformAction (enmSomeAction.Open) สองครั้งติดต่อกันการเรียกครั้งที่สองควรใช้ InvalidOperationException (เนื่องจากการพิสูจน์ถูกต้อง แต่ไม่ใช่สถานะปัจจุบันของการควบคุม)

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

public void SomeMethod()
{
    If (m_Disposed) {
          throw new ObjectDisposedException("Object has been disposed")
     }
    // ... Normal execution code
}

อัปเดต:เพื่อตอบการติดตามของคุณ: เป็นสถานการณ์ที่ไม่ชัดเจนและมีความซับซ้อนเล็กน้อยโดยประเภทข้อมูลทั่วไป (ไม่ได้อยู่ใน. NET Generics Sense) ที่ใช้เพื่อแสดงชุดข้อมูลเฉพาะ วัตถุ enum หรือวัตถุที่มีการพิมพ์อื่น ๆ จะเหมาะกว่า - แต่เราไม่ได้ควบคุมมันเสมอไป

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


1
จุดดี. สิ่งนี้อาจอธิบายความแตกต่างระหว่างอินพุตที่ไม่ถูกต้องและไม่ได้รับการตรวจสอบ +1
Daniel Brückner

Psst ฉันเห็นด้วยกับคุณเพียงแค่จะไม่ขโมยฟ้าร้องของคุณ แต่เมื่อคุณชี้ให้เห็นฉันจึงปรับปรุงคำตอบของฉัน
JoshBerke

38

ขึ้นอยู่กับมูลค่าที่แท้จริงและข้อยกเว้นที่เหมาะสมที่สุด:

  • ArgumentException (มีบางอย่างผิดปกติกับมูลค่า)

  • ArgumentNullException (อาร์กิวเมนต์เป็นโมฆะในขณะที่ไม่อนุญาต)

  • ArgumentOutOfRangeException (อาร์กิวเมนต์มีค่าอยู่นอกช่วงที่ถูกต้อง)

หากนี่ยังไม่แม่นยำพอเพียงให้คลาสยกเว้นของคุณมา ArgumentExceptionถ้าไม่แม่นยำพอเพียงได้รับมาชั้นยกเว้นของคุณเองจาก

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


6
นำมาจากหน้าของ MSDN ใน InvalidOperationException: "InvalidOperationException ถูกใช้ในกรณีที่ความล้มเหลวในการเรียกใช้เมธอดเกิดจากเหตุผลอื่นที่ไม่ใช่อาร์กิวเมนต์ที่ไม่ถูกต้อง"
STW


3

ArgumentException :

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

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


1

คำตอบสั้น:
ทั้ง

คำตอบอีกต่อไป: การ
ใช้ Argument * Exception (ยกเว้นในไลบรารีที่เป็นผลิตภัณฑ์ที่อยู่บนเช่นไลบรารีคอมโพเนนต์) เป็นกลิ่น ข้อยกเว้นคือการจัดการกับสถานการณ์ที่ยอดเยี่ยมไม่ใช่ข้อบกพร่องและไม่ใช่ของผู้ใช้ (เช่นผู้บริโภค API)

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

  • ยืนยันไม่จำเป็นต้องมีการทดสอบในขณะที่โยนยืนยันทำและทดสอบกับ ArgumentNullException ดูไร้สาระ (ลอง)
  • ยืนยันการสื่อสารที่ดีกว่าการใช้งานที่ตั้งใจของหน่วยและอยู่ใกล้กับเอกสารประกอบการปฏิบัติการกว่าสเปคพฤติกรรมการเรียน
  • คุณสามารถเปลี่ยนพฤติกรรมการละเมิดการยืนยัน ตัวอย่างเช่นในการรวบรวมการดีบั๊กกล่องข้อความนั้นดีดังนั้น QA ของคุณจะโดนมันทันที (คุณได้รับ IDE ของคุณแตกหักในบรรทัดที่มันเกิดขึ้น) ในขณะที่การทดสอบหน่วยคุณสามารถระบุความล้มเหลวในการยืนยันว่าเป็นการทดสอบล้มเหลว .

นี่คือสิ่งที่การจัดการกับข้อยกเว้นเป็นโมฆะดูเหมือน (แดกดันชัด):

try {
    library.Method(null);
}
catch (ArgumentNullException e) {
    // retry with real argument this time
    library.Method(realArgument);
}

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

BTW: ในกรณีนี้คุณสามารถใช้ประเภทเดือนแทนที่จะเป็น int C # สั้นเมื่อพูดถึงความปลอดภัย (Aspect # rulez!) แต่บางครั้งคุณสามารถป้องกัน (หรือจับในเวลารวบรวม) ข้อผิดพลาดเหล่านั้นทั้งหมดเข้าด้วยกัน

และใช่ MicroSoft ผิดเกี่ยวกับเรื่องนั้น


6
IMHO ควรโยนข้อยกเว้นเมื่อวิธีการที่เรียกไม่สามารถดำเนินการอย่างสมเหตุสมผล ซึ่งรวมถึงกรณีเมื่อผู้โทรได้ผ่านข้อโต้แย้งปลอม คุณจะทำอะไรแทน ส่งคืน -1
จอห์นแซนเดอ

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

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

0

มี ArgumentException มาตรฐานที่คุณสามารถใช้หรือคุณสามารถคลาสย่อยและสร้างของคุณเอง มีคลาส ArgumentException เฉพาะหลายคลาส:

http://msdn.microsoft.com/en-us/library/system.argumentexception(VS.71).aspx

แล้วแต่อย่างใดอย่างหนึ่งทำงานได้ดีที่สุด


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

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

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