ทำไมจึงต้องออกแบบภาษาสมัยใหม่โดยไม่มีกลไกการจัดการยกเว้น?


47

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

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

ยกตัวอย่างเช่นฉันจะแสดงสิ่งที่ดีที่สุดได้อย่างไร

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
     # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

ในภาษา (ตัวอย่างเช่น Swift) ที่ไม่มีการจัดการข้อยกเว้น?


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

3
คำตอบสำหรับคำถามของคุณคือคุณต้องการการสนับสนุนทางภาษาสำหรับข้อยกเว้นเพื่อเขียน การสนับสนุนด้านภาษาโดยทั่วไปประกอบด้วยการจัดการหน่วยความจำ เนื่องจากข้อยกเว้นสามารถโยนได้ทุกที่และจับได้ทุกที่จึงจำเป็นต้องมีวิธีกำจัดวัตถุที่ไม่ต้องอาศัยการควบคุมการไหล
Robert Harvey

1
ฉันไม่ได้ติดตามคุณเลย @Robert C ++ จัดการเพื่อสนับสนุนข้อยกเว้นโดยไม่มีการรวบรวมขยะ
Karl Bielefeldt

3
@KarlBielefeldt: ค่าใช้จ่ายที่ดีจากสิ่งที่ฉันเข้าใจ จากนั้นอีกครั้งมีอะไรบ้างที่ดำเนินการใน C ++ โดยไม่มีค่าใช้จ่ายจำนวนมากอย่างน้อยก็ในความพยายามและต้องการความรู้ด้านโดเมนหรือไม่
Robert Harvey

2
@ RobertHarvey: จุดดี ฉันเป็นหนึ่งในคนที่ไม่ได้คิดหนักพอเกี่ยวกับสิ่งเหล่านี้ ฉันถูกกล่อมให้คิดว่า ARC คือ GC แต่แน่นอนว่าไม่ใช่ ดังนั้นโดยทั่วไป (ถ้าฉันเข้าใจโดยประมาณ) ข้อยกเว้นจะเป็นสิ่งที่ยุ่งเหยิง (C ++ อย่างไรก็ตาม) ในภาษาที่การกำจัดวัตถุอาศัยการควบคุมการไหล
orome

คำตอบ:


33

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

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

เหตุผลอื่นคือฟังก์ชั่นชั้นหนึ่งอนุญาตให้สร้างเช่นตัวเลือกและฟิวเจอร์สที่ให้คุณประโยชน์ syntactic ของข้อยกเว้นที่มีความยืดหยุ่นมากขึ้น กล่าวอีกนัยหนึ่งภาษาที่เหลือมีความหมายมากพอที่ข้อยกเว้นจะไม่ซื้อคุณ

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

นี่คือตัวอย่างการใช้บทช่วยสอนFutureจากScala นี้ :

val f: Future[List[String]] = future {
  session.getRecentPosts
}
f onFailure {
  case t => println("An error has occured: " + t.getMessage)
}
f onSuccess {
  case posts => for (post <- posts) println(post)
}

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

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

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

นี่คือสิ่งต่าง ๆ ที่คุณ "ได้รับจากการสูญเสียข้อยกเว้น"


1
ดังนั้นจึงFutureเป็นวิธีการตรวจสอบค่าที่ส่งคืนจากฟังก์ชันโดยไม่หยุดเพื่อรอ เช่นเดียวกับ Swift ซึ่งเป็นค่าตอบแทนตาม แต่ต่างจาก Swift การตอบสนองต่อค่าส่งคืนสามารถเกิดขึ้นได้ในภายหลัง (เล็กน้อยเช่นข้อยกเว้น) ขวา?
orome

1
คุณเข้าใจFutureถูกต้อง แต่ฉันคิดว่าคุณอาจเข้าใจผิด Swift ดูส่วนแรกของคำตอบ stackoverflow นี้ตัวอย่าง
Karl Bielefeldt

อืมฉันยังใหม่กับ Swift ดังนั้นคำตอบนั้นค่อนข้างยากสำหรับฉันที่จะแยกวิเคราะห์ แต่ถ้าฉันไม่เข้าใจผิด: นั่นคือการส่งผ่าน handler ที่สามารถเรียกใช้ในภายหลัง ขวา?
orome

ใช่. คุณกำลังสร้างการติดต่อกลับเมื่อเกิดข้อผิดพลาด
Karl Bielefeldt

Eitherจะเป็นตัวอย่างที่ดีกว่า IMHO
PawełPrażak

19

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

cleanMug();
brewCoffee();
pourCoffee();
drinkCoffee();

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

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

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

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


ถูกต้องหรือไม่ประเภทของระบบพิมพ์ที่ Swift มีรวมถึงตัวเลือกเป็นประเภทของ "ระบบพิมพ์ทรงพลัง" ที่ประสบความสำเร็จหรือไม่
orome

2
ใช่ตัวเลือกและโดยทั่วไปประเภทผลรวม (เรียกว่า "enum" ใน Swift / Rust) สามารถบรรลุสิ่งนี้ได้ มันต้องใช้งานพิเศษเพื่อทำให้พวกมันพอใจในการใช้งาน: ใน Swift สิ่งนี้สามารถทำได้ด้วยไวยากรณ์การโยงแบบเสริม ใน Haskell สิ่งนี้สามารถทำได้ด้วยการทำเครื่องหมายแบบ monadic
Rufflewind

"ระบบที่มีประสิทธิภาพเพียงพอ" สามารถให้การติดตามแบบสแต็กถ้าไม่ใช่ระบบที่ไร้ประโยชน์จริงๆ
PawełPrażak

1
+1 สำหรับการชี้ให้เห็นว่าข้อยกเว้นปิดบังการไหลของการควบคุม เช่นเดียวกับโน้ต auxilary: มันยืนเพื่อเหตุผลที่ไม่ว่าจะเป็นข้อยกเว้นไม่ได้ชั่วร้ายจริงมากกว่าgotoการgotoถูก จำกัด ให้ฟังก์ชั่นเดียวซึ่งเป็นช่วงที่สวยขนาดเล็กฟังก์ชั่นที่มีให้มันมีขนาดเล็กยกเว้นทำหน้าที่มากขึ้นเช่นข้ามบางgotoและcome from(ดูen.wikipedia.org/wiki/INTERCAL ;-)) มันสามารถเชื่อมต่อโค้ดสองส่วนใด ๆ ได้มากอาจข้ามฟังก์ชั่นที่สามของรหัสได้ สิ่งเดียวที่มันทำไม่ได้ซึ่งgotoทำได้คือจะกลับไป
cmaster

2
@ PawełPrażakเมื่อทำงานกับฟังก์ชั่นการสั่งซื้อที่สูงขึ้นจำนวนการติดตามสแต็กนั้นไม่ได้มีค่ามากนัก การรับประกันที่แข็งแกร่งเกี่ยวกับอินพุตและเอาต์พุตและการหลีกเลี่ยงผลข้างเคียงเป็นสิ่งที่ป้องกันไม่ให้เกิดความสับสนในทางอ้อมนี้
แจ็ค

11

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

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

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

ดังนั้นภาษาที่ทันสมัยบางคนตัดสินใจว่ามันไม่คุ้มค่า ยกตัวอย่างเช่นสนิมมีข้อยกเว้น แต่ไม่สามารถ "จับได้" ดังนั้นจึงไม่มีวิธีใดที่รหัสในการโต้ตอบกับวัตถุในสถานะที่ไม่ถูกต้อง


1
วัตถุประสงค์ของการจับคือการทำให้วัตถุที่สอดคล้องกัน (หรือยุติบางอย่างถ้ามันเป็นไปไม่ได้) หลังจากเกิดข้อผิดพลาด
JoulinRouge

@JoulinRouge ฉันรู้ แต่บางภาษาได้ตัดสินใจที่จะไม่ให้โอกาสคุณ แต่กลับทำให้กระบวนการทั้งหมดพัง นักออกแบบภาษาเหล่านั้นรู้ว่าการล้างข้อมูลแบบไหนที่คุณต้องการ แต่สรุปว่ามันยากเกินกว่าที่จะให้คุณและการแลกเปลี่ยนที่เกี่ยวข้องกับการทำเช่นนั้นจะไม่คุ้มค่า ฉันรู้ว่าคุณอาจไม่เห็นด้วยกับการเลือกของพวกเขา ... แต่มันมีค่าที่จะรู้ว่าพวกเขาเลือกอย่างมีเหตุผลด้วยเหตุผลเหล่านี้
Charlie Flowers

6

สิ่งหนึ่งที่ฉันรู้สึกประหลาดใจในตอนแรกเกี่ยวกับภาษารัสต์คือมันไม่รองรับข้อยกเว้นการตรวจจับ คุณสามารถโยนข้อยกเว้นได้ แต่รันไทม์เท่านั้นที่สามารถตรวจจับได้เมื่องาน (คิดว่าเธรด แต่ไม่ได้แยกเธรด OS) เสมอไป ถ้าคุณเริ่มต้นงานด้วยตัวคุณเองแล้วคุณสามารถถามว่ามันออกมาได้ตามปกติหรือไม่ว่าจะfail!()ed

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

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

use std::io::IoResult;
use std::io::File;

fn load_file(name: &Path) -> IoResult<String>
{
    let mut file = try!(File::open(name));
    let s = try!(file.read_to_string());
    return Ok(s);
}

fn main()
{
    print!("{}", load_file(&Path::new("/tmp/hello")).unwrap());
}

1
ดังนั้นมันยุติธรรมที่จะบอกว่าสิ่งนี้เช่นกัน (เช่นแนวทางของ Go) คล้ายกับ Swift ซึ่งมีassertแต่ไม่catch?
orome

1
ใน Swift ลอง! หมายความว่า: ใช่ฉันรู้ว่าสิ่งนี้อาจล้มเหลว แต่ฉันแน่ใจว่ามันจะไม่เป็นเช่นนั้นดังนั้นฉันจึงไม่จัดการกับมันและหากมันล้มเหลวรหัสของฉันก็ไม่ถูกต้องดังนั้นโปรดล้มเหลวในกรณีนั้น
gnasher729

6

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

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

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

ที่จริงแล้วความหมายของ "คาดไม่ถึง"

Btw. สิ่งที่คาดหวังและสิ่งที่ไม่ใช่การตัดสินใจที่สามารถทำได้ที่ไซต์การโทรเท่านั้น นั่นเป็นเหตุผลที่ข้อยกเว้นที่ตรวจสอบใน Java ไม่ได้ผลการตัดสินใจจะทำในเวลาที่พัฒนา API เมื่อมันไม่ชัดเจนในสิ่งที่คาดหวังหรือไม่คาดคิด

ตัวอย่างง่ายๆ: API ของแฮชแผนที่มีสองวิธี:

Value get(Key)

และ

Option<Value> getOption(key)

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

รหัสไม่ควรพยายามจัดการกับสมมติฐานพื้นฐานที่ล้มเหลว

ยกเว้นโดยการตรวจสอบพวกเขาและโยนข้อยกเว้นที่อ่านได้ดีแน่นอน

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

บล็อกที่จับได้ทั่วสถานที่นั้นเป็นความคิดที่แย่มาก

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

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

และบางคนเข้าใจผิดเกี่ยวกับสิ่งที่พวกเขาดีจริงๆ

การจำลองพวกเขาด้วยการทำทุกอย่างผ่านการเชื่อมแบบ monadic ทำให้โค้ดของคุณอ่านง่ายและบำรุงรักษาน้อยลงและคุณก็จบลงด้วยการไม่มีร่องรอยการติดตามซึ่งทำให้วิธีนี้เลวร้ายลง

การจัดการข้อผิดพลาดของรูปแบบการทำงานนั้นยอดเยี่ยมสำหรับกรณีข้อผิดพลาดที่คาดไว้

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


3

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

Swift มีคุณสมบัติที่ดูเหมือนการจัดการข้อยกเว้น แต่เป็นเพียงการบังคับใช้ข้อผิดพลาดในการจัดการ ในอดีต Objective-C มีรูปแบบการจัดการข้อผิดพลาดที่แพร่หลาย: วิธีการหนึ่งจะส่งคืน BOOL (YES เพื่อความสำเร็จ) หรือการอ้างอิงวัตถุ (ไม่มีศูนย์สำหรับความล้มเหลวไม่ใช่ศูนย์เพื่อความสำเร็จ) และมีพารามิเตอร์ "ตัวชี้ไปยัง NSError *" ซึ่งจะใช้ในการจัดเก็บการอ้างอิง NSError Swift แปลงการโทรไปยังวิธีการดังกล่าวโดยอัตโนมัติเป็นสิ่งที่ดูเหมือนการจัดการข้อยกเว้น

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


สำหรับ Swift นี่เป็นคำตอบที่ถูกต้องนั่นคือ IMO Swift ต้องเข้ากันได้กับกรอบระบบ Objective-C ที่มีอยู่ดังนั้นภายใต้ประทุนพวกเขาไม่มีข้อยกเว้นแบบดั้งเดิม ฉันเขียนบล็อกโพสต์เมื่อไม่นานมานี้เกี่ยวกับวิธีการทำงานของ ObjC สำหรับการจัดการข้อผิดพลาด: orangejuiceliberationfront.com/ …
uliwitness

2
 int result;
 if((result = operation_that_can_throw_ioerror()) == IOError)
 {
  handle_the_exception_somehow();
 }
 else
 {
   # we don't want to catch the IOError if it's raised
   result = another_operation_that_can_throw_ioerror();
 }
 result |= something_we_always_need_to_do();
 return result;

ใน C คุณจะจบลงด้วยบางสิ่งที่เหมือนข้างต้น

มีสิ่งใดบ้างที่ฉันไม่สามารถทำได้ใน Swift ที่ฉันสามารถทำได้ด้วยข้อยกเว้น

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


และในทำนองเดียวกันผู้ที่เรียกร้องให้...throw_ioerror()กลับข้อผิดพลาดมากกว่าการโยนข้อยกเว้น?
orome

1
@raxacoricofallapatorius หากเราอ้างสิทธิ์ในข้อยกเว้นไม่มีอยู่ฉันถือว่าโปรแกรมทำตามรูปแบบปกติของการส่งคืนรหัสข้อผิดพลาดเมื่อความล้มเหลวและ 0 เมื่อสำเร็จ
stonemetal

1
@stonemetal บางภาษาเช่น Rust และ Haskell ใช้ระบบ type เพื่อส่งคืนสิ่งที่มีความหมายมากกว่ารหัสข้อผิดพลาดโดยไม่ต้องเพิ่มจุดออกที่ซ่อนอยู่ในแบบที่ยกเว้น ตัวอย่างเช่นฟังก์ชัน Rust อาจส่งคืนResult<T, E>enum ซึ่งสามารถเป็นได้ทั้งแบบOk<T>หรือแบบErr<E>มีTชนิดที่คุณต้องการถ้ามีและEเป็นประเภทที่แสดงถึงข้อผิดพลาด การจับคู่รูปแบบและวิธีการบางอย่างทำให้การจัดการทั้งความสำเร็จและความล้มเหลวง่ายขึ้น ในระยะสั้นอย่าถือว่าไม่มีข้อยกเว้นโดยอัตโนมัติหมายถึงรหัสข้อผิดพลาด
8bittree

1

นอกจากคำตอบของ Charlie:

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

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

ตัวอย่างเช่นเมื่อคุณต้องจัดการกับ IO โดยใช้การเข้ารหัสบางอย่างคุณอาจมีข้อยกเว้น 20 ชนิดที่อาจถูกโยนออกมาจาก 50 วิธี ลองนึกภาพจำนวนรหัสการจัดการข้อยกเว้นที่คุณต้องการ การจัดการข้อยกเว้นจะใช้รหัสมากกว่าโค้ดมากกว่าสองเท่า

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


ในความเป็นจริงแล้วข้อยกเว้นเหล่านี้สามารถจัดการได้ในที่เดียว ตัวอย่างเช่นในฟังก์ชั่น "download data update" หาก SSL ล้มเหลวหรือ DNS ไม่สามารถแก้ไขได้หรือเว็บเซิร์ฟเวอร์คืนค่า 404 มันไม่สำคัญเลยจับที่ด้านบนและรายงานข้อผิดพลาดให้ผู้ใช้
Zan Lynx
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.