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


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.