ข้อยกเว้นที่ดีที่สุดสำหรับอาร์กิวเมนต์ประเภททั่วไปที่ไม่ถูกต้อง


106

ฉันกำลังเขียนโค้ดสำหรับUnconstrainedMelodyซึ่งมีวิธีการทั่วไปที่จะทำกับ enums

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

เพื่อให้เป็นรูปธรรมถ้าฉันมีสิ่งนี้:

// Returns a value with all bits set by any values
public static T GetBitMask<T>() where T : struct, IEnumConstraint
{
    if (!IsFlags<T>()) // This method doesn't throw
    {
        throw new ???
    }
    // Normal work here
}

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

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


วันนี้ฉันสะดุดกับเรื่องนี้ในการเขียนวิธีการทั่วไปโดยวางข้อกำหนดพิเศษไว้ในประเภทที่ใช้ซึ่งไม่สามารถอธิบายด้วยข้อ จำกัด ได้ ฉันรู้สึกประหลาดใจที่ไม่พบประเภทข้อยกเว้นที่มีอยู่แล้วใน BCL แต่ปัญหาที่กลืนไม่เข้าคายไม่ออกนี้เป็นสิ่งที่ฉันต้องเผชิญเมื่อสองสามวันก่อนในโครงการเดียวกันสำหรับผลิตภัณฑ์ทั่วไปที่จะใช้ได้กับแอตทริบิวต์ Flags เท่านั้น น่ากลัว!
— Andras Zoltan

คำตอบ:


46

NotSupportedException ดูเหมือนจะเข้ากันได้ดี แต่เอกสารระบุชัดเจนว่าควรใช้เพื่อวัตถุประสงค์อื่น จากข้อสังเกตของคลาส MSDN:

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

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

ระบุวัตถุประสงค์ของUnconstrained Melody ...

มีสิ่งที่เป็นประโยชน์มากมายที่สามารถทำได้ด้วยวิธีการ / คลาสทั่วไปซึ่งมีข้อ จำกัด ประเภท "T: enum" หรือ "T: delegate" - แต่น่าเสียดายที่สิ่งเหล่านี้ไม่ได้รับอนุญาตใน C #

ไลบรารียูทิลิตี้นี้ทำงานตามข้อห้ามโดยใช้ ildasm / ilasm ...

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

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


ในความเป็นจริงมันเกือบจะเป็นเรื่องยากที่จะโยนข้อยกเว้นภายในเท่านั้นในตอนแรกในลักษณะเดียวกับที่ Code Contracts ทำ ... ฉันไม่เชื่อว่าจะมีใครจับได้
— Jon Skeet

เสียดายที่คืนค่าว่างไม่ได้!
— Jeff Sternal

25
ฉันจะใช้ TypeArgumentException
— Jon Skeet

การเพิ่มข้อยกเว้นใน Framework อาจมี "ภาระการพิสูจน์" สูง แต่การกำหนดข้อยกเว้นแบบกำหนดเองไม่ควร สิ่งที่ชอบInvalidOperationExceptionเป็นเรื่องน่าเบื่อเพราะ "Foo ขอให้แถบคอลเลกชันเพิ่มสิ่งที่มีอยู่แล้ว Bar จึงโยน IOE" และ "Foo ขอให้ collection Bar เพิ่มบางอย่างดังนั้น Bar จึงเรียก Boz ซึ่งพ่น IOE แม้ว่า Bar จะไม่ได้คาดหวังก็ตาม" ทั้งสองจะโยนข้อยกเว้นประเภทเดียวกัน รหัสที่คาดว่าจะจับได้ก่อนจะไม่คาดหวังในภายหลัง ที่มีการกล่าวว่า ...
— supercat

... ฉันคิดว่าข้อโต้แย้งที่สนับสนุนข้อยกเว้น Framework ที่นี่น่าสนใจกว่าข้อยกเว้นที่กำหนดเอง ลักษณะทั่วไปของ NSE คือเมื่อมีการอ้างอิงถึงอ็อบเจ็กต์เป็นประเภททั่วไปและบางประเภท แต่ไม่ใช่ทั้งหมดของอ็อบเจ็กต์เฉพาะที่จุดอ้างอิงจะรองรับความสามารถโดยพยายามใช้ความสามารถในประเภทเฉพาะซึ่งไม่ ไม่สนับสนุนควรโยน NSE ฉันจะถือว่า a Foo<T>เป็น "ประเภททั่วไป" และFoo<Bar>เป็น "ประเภทเฉพาะ" ในบริบทนั้นแม้ว่าจะไม่มีความสัมพันธ์ "การสืบทอด" ระหว่างกันก็ตาม
— supercat

25

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

ฉันคิดว่า InvalidOperationException เป็นข้อยกเว้นที่เหมาะสมที่สุดที่คุณสามารถทำได้


ขอขอบคุณสำหรับความคืบหน้าเกี่ยวกับ NSE ยินดีต้อนรับข้อมูลจากเพื่อนร่วมงานของคุณด้วย btw ...
— Jon Skeet

ประเด็นคือฟังก์ชันที่จอนต้องการไม่มีอะไรที่คล้ายกันใน BCL คอมไพเลอร์น่าจะจับได้ หากคุณลบข้อกำหนด "คุณสมบัติ" ออกจาก NotSupportedException สิ่งที่คุณกล่าวถึง (เช่นคอลเลกชัน ReadOnly) เป็นสิ่งที่ใกล้เคียงที่สุดกับปัญหาของจอน
— Mehrdad Afshari

จุดหนึ่ง - ฉันมีวิธี IsFlags (มันจะต้องมีวิธีการที่จะเป็นทั่วไป) ซึ่งเป็นการจัดเรียงของการแสดงให้เห็นว่าการดำเนินการชนิดนี้จะไม่ได้รับการสนับสนุน ... ดังนั้นในความรู้สึก NSE ว่าจะมีความเหมาะสม เช่นผู้โทรสามารถตรวจสอบได้ก่อน
— Jon Skeet

@ จอน: ฉันคิดว่าแม้ว่าคุณจะไม่มีทรัพย์สินเช่นนี้แต่สมาชิกทุกคนในประเภทของคุณอาศัยความจริงที่ได้Tรับการenumตกแต่งด้วยโดยเนื้อแท้Flagsแล้วมันก็ถูกต้องที่จะโยน NSE
— Mehrdad Afshari

1
@ จอน: StupidClrExceptionสร้างชื่อสนุก ๆ ;)
— Mehrdad Afshari

13

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

อัปเดต

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

หากความล้มเหลวไม่เกี่ยวข้องกับอาร์กิวเมนต์ด้วยตัวเองควรใช้ InvalidOperationException

มีความเชื่ออย่างน้อยหนึ่งอย่างในนั้นคำแนะนำพารามิเตอร์วิธีการนั้นจะถูกนำไปใช้กับพารามิเตอร์ทั่วไปด้วย แต่ก็ไม่มีอะไรดีไปกว่าใน SystemException hierachy imho


1
ไม่ไม่มีทางที่จะ จำกัด เวลาคอมไพล์ได้ IsFlag<T>พิจารณาว่า enum [FlagsAttribute]ใช้กับมันหรือไม่และ CLR ไม่มีข้อ จำกัด ตามแอตทริบิวต์ มันจะอยู่ในโลกที่สมบูรณ์แบบ - หรือมีวิธีอื่นที่จะ จำกัด - แต่ในกรณีนี้มันไม่ได้ผล :(
— Jon Skeet

(+1 สำหรับหลักการทั่วไปแม้ว่า - ฉันรักที่จะสามารถ . เพื่อ จำกัด ได้)
— จอนสกีต

9

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


2
NotSupportedException ถูกใช้เพื่อวัตถุประสงค์ที่แตกต่างกันมากใน BCL มันไม่พอดีกับที่นี่ blogs.msdn.com/jaredpar/archive/2008/12/12/…
— JaredPar

8

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

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


อืม. มันยังไม่ค่อยถูกปากนัก แต่ฉันคิดว่ามันจะเป็นสิ่งที่ใกล้เคียงที่สุด
— Jon Skeet

จอน: มันไม่ถูกต้องเพราะเราคาดหวังว่าคอมไพเลอร์จะจับได้
— Mehrdad Afshari

ได้. นี่เป็นข้อ จำกัด แปลก ๆ ที่ฉันอยากจะใช้ แต่ทำไม่ได้ :)
— Jon Skeet

6

เห็นได้ชัดว่า Microsoft ใช้ArgumentExceptionสำหรับสิ่งนั้นดังที่แสดงในตัวอย่างของExpression.Lambda <> , Enum.TryParse <>หรือMarshal.GetDelegateForFunctionPointer <>ในส่วนข้อยกเว้น ฉันไม่พบตัวอย่างที่ระบุเป็นอย่างอื่น (แม้จะค้นหาแหล่งอ้างอิงในท้องถิ่นสำหรับTDelegateและTEnum)

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

หวังว่ามันจะตัดสินคำถามทุกครั้ง


ตัวอย่างเดียวในเฟรมเวิร์กไม่เพียงพอสำหรับฉันไม่ - เนื่องจากจำนวนสถานที่ที่ฉันคิดว่า MS เลือกได้ไม่ดีในกรณีอื่น ๆ :) ฉันจะไม่ได้มาTypeArgumentExceptionจากArgumentExceptionเพียงเพราะอาร์กิวเมนต์ประเภทไม่ใช่เรื่องปกติ การโต้เถียง.
— Jon Skeet

1
แน่นอนว่าน่าสนใจกว่าในแง่ของ "มันคือสิ่งที่ MS ทำอย่างสม่ำเสมอ" มันไม่ได้ทำให้มันน่าสนใจอีกต่อไปในแง่ของการจับคู่เอกสาร ... และฉันรู้ว่ามีคนมากมายในทีม C # ที่ใส่ใจอย่างลึกซึ้งเกี่ยวกับความแตกต่างระหว่างอาร์กิวเมนต์ปกติและอาร์กิวเมนต์ประเภท :) แต่ขอบคุณสำหรับตัวอย่าง - พวกเขามีประโยชน์มาก
— Jon Skeet

@ จอน Skeet: ทำการแก้ไข; ตอนนี้มี 3 ตัวอย่างจากไลบรารี MS ที่แตกต่างกันทั้งหมดที่มี ArgumentException จัดทำเป็นเอกสารที่ถูกโยนทิ้ง ดังนั้นหากเป็นทางเลือกที่ไม่ดีอย่างน้อยก็เป็นทางเลือกที่ไม่ดีอย่างสม่ำเสมอ ;) ฉันเดาว่า Microsoft ถือว่าอาร์กิวเมนต์ปกติและอาร์กิวเมนต์ประเภทเป็นอาร์กิวเมนต์ทั้งคู่ และโดยส่วนตัวแล้วฉันคิดว่าสมมติฐานดังกล่าวค่อนข้างสมเหตุสมผล ^^ '
— Alice

อ่าไม่เป็นไรดูเหมือนว่าคุณสังเกตเห็นแล้ว ดีใจที่ช่วยได้ ^^
— Alice

ฉันคิดว่าเราจะต้องตกลงที่จะไม่เห็นด้วยว่ามันสมเหตุสมผลหรือไม่ที่จะปฏิบัติต่อพวกเขาเหมือนเดิม พวกเขาไม่เหมือนกันอย่างแน่นอนเมื่อพูดถึงการไตร่ตรองหรือกฎเกณฑ์ทางภาษา ฯลฯ ... พวกมันได้รับการจัดการที่แตกต่างกันมาก
— Jon Skeet


2

การโยนข้อยกเว้นที่กำหนดเองควรกระทำในทุกกรณีที่เป็นปัญหา ข้อยกเว้นที่กำหนดเองจะใช้ได้เสมอไม่ว่าผู้ใช้ API จะต้องการอะไรก็ตาม นักพัฒนาสามารถจับข้อยกเว้นประเภทใดก็ได้หากเขาไม่สนใจ แต่หากนักพัฒนาต้องการการจัดการพิเศษเขาจะเป็น SOL


นอกจากนี้นักพัฒนาควรบันทึกข้อยกเว้นทั้งหมดที่แสดงไว้ในความคิดเห็น XML
— Eric Schneider

1

วิธีการสืบทอดจาก NotSupportedException ในขณะที่ฉันเห็นด้วยกับ @Mehrdad ว่ามันสมเหตุสมผลที่สุด แต่ฉันได้ยินประเด็นของคุณว่ามันดูไม่ลงตัว ดังนั้นรับมรดกจาก NotSupportedException และวิธีนี้ผู้ที่เข้ารหัสกับ API ของคุณยังสามารถจับ NotSupportedException ได้


1

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

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

หากผู้ใช้พยายามส่งผ่านสิ่งที่ไม่ใช่ enum ฉันจะโยน InvalidOperationException

แก้ไข:

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

ฉันยังเห็น NotSupportedExceptions ถูกโยนทิ้งเป็นข้อยกเว้นการให้สิทธิ์การใช้งาน "คุณกำลังใช้งานซอฟต์แวร์เวอร์ชันฟรีนี้ไม่รองรับฟังก์ชันนี้"

แก้ไข 2:

อีกหนึ่งที่เป็นไปได้:

System.ComponentModel.InvalidEnumArgumentException  

เกิดข้อยกเว้นเมื่อใช้อาร์กิวเมนต์ที่ไม่ถูกต้องซึ่งเป็นตัวแจงนับ


ฉันจะ จำกัด มันให้เป็น enum (หลังจากตลกขบขัน) - มันเป็นเพียงธงที่ฉันกังวล
— จอนสกีต

ผมคิดว่าผู้คนออกใบอนุญาตควรโยนตัวอย่างของระดับการสืบทอดจากLicensingException InvalidOperationException
— Mehrdad Afshari

ฉันเห็นด้วยกับ Mehrdad แต่น่าเสียดายที่ข้อยกเว้นเป็นหนึ่งในพื้นที่ที่มีสีเทาอยู่ในกรอบ แต่ฉันแน่ใจว่านี่เหมือนกันสำหรับหลายภาษา (ไม่ได้บอกว่าผมอยากกลับไปข้อผิดพลาด runtime VB6 13 ฮิฮิ)
— ปีเตอร์

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