ฉันคิดเกี่ยวกับมันและไม่สามารถมากับตัวอย่าง ทำไมบางคนถึงอยากจะมีข้อยกเว้นและไม่ทำอะไรเลย คุณยกตัวอย่างได้ไหม อาจเป็นเพียงบางสิ่งที่ไม่ควรทำ
ฉันคิดเกี่ยวกับมันและไม่สามารถมากับตัวอย่าง ทำไมบางคนถึงอยากจะมีข้อยกเว้นและไม่ทำอะไรเลย คุณยกตัวอย่างได้ไหม อาจเป็นเพียงบางสิ่งที่ไม่ควรทำ
คำตอบ:
ฉันทำมันตลอดเวลากับสิ่งต่าง ๆ เช่นข้อผิดพลาดในการแปลงใน D:
import std.conv, std.stdio, std.exception;
void main(string[] args) {
enforce(args.length > 1, "Usage: foo.exe filename");
double[] nums;
// Process a text file with one number per line into an array of doubles,
// ignoring any malformed lines.
foreach(line; File(args[1]).byLine()) {
try {
nums ~= to!double(line);
} catch(ConvError) {
// Ignore malformed lines.
}
}
// Do stuff with nums.
}
ที่กล่าวว่าฉันคิดว่าบล็อกทั้งหมดควรมีบางสิ่งบางอย่างในนั้นแม้ว่าสิ่งนั้นเป็นเพียงความคิดเห็นอธิบายว่าทำไมคุณไม่สนใจข้อยกเว้น
แก้ไข: ฉันต้องการเน้นว่าหากคุณจะทำสิ่งนี้คุณควรระวังที่จะรับเฉพาะข้อยกเว้นเฉพาะที่คุณต้องการเพิกเฉย การทำสิ่งเก่า ๆcatch {}
นั้นเป็นเรื่องที่ไม่ดี
ตัวอย่างหนึ่งที่ฉันคิดว่าก็โอเคที่จะกลืนข้อยกเว้นโดยไม่ทำอะไรเลยแม้แต่การบันทึกข้อยกเว้นอยู่ภายในรหัสการบันทึกเอง
หากคุณพยายามที่จะบันทึกบางสิ่งบางอย่างและได้รับการยกเว้นมีไม่มากที่คุณสามารถทำได้:
นั่นทำให้คุณมีตัวเลือก "ความชั่วร้ายน้อยที่สุด" ในการกลืนมันอย่างเงียบ ๆ ทำให้แอปพลิเคชันสามารถทำงานหลักได้อย่างต่อเนื่อง แต่ลดความสามารถในการแก้ไขปัญหา
หากมีข้อผิดพลาดเกิดขึ้นจากการดำเนินการที่เป็นทางเลือกอย่างไรก็ตามอาจไม่มีอะไรต้องทำ อาจไม่มีประโยชน์ในการบันทึก ในกรณีนี้คุณอาจต้องการจับและไม่ทำอะไรเลย
หากคุณกำลังทำสิ่งนี้รวมถึงความคิดเห็น แสดงความคิดเห็นทุกสิ่งที่ดูเหมือนเป็นข้อบกพร่องเสมอ (การผิดพลาดในswitch
คำสั่งcatch
บล็อกว่างเปล่าการกำหนดเงื่อนไข)
คุณอาจยืนยันว่านี่ไม่ใช่การใช้ข้อยกเว้นที่เหมาะสม แต่เป็นคำถามอื่น
catch
บล็อกเปล่าเป็นกลิ่นรหัสในภาษาส่วนใหญ่ แนวคิดหลักคือการใช้ข้อยกเว้นสำหรับสถานการณ์พิเศษและไม่ใช้สำหรับการควบคุมเชิงตรรกะ ข้อยกเว้นทั้งหมดจะต้องได้รับการจัดการที่ไหนสักแห่ง
เป็นผลมาจากการใช้วิธีการนี้เมื่อคุณตั้งโปรแกรมเลเยอร์แอปพลิเคชันหนึ่งโดยเฉพาะคุณมีหลายทางเลือก:
สิ่งนี้ไม่ได้ออกจากห้องเพื่อcatch
บล็อกที่ว่างเปล่า
แก้ไขเพื่อเพิ่ม : สมมติว่าคุณกำลังเขียนโปรแกรมในภาษาที่การโยนข้อยกเว้นเป็นวิธีปกติในการควบคุมตรรกะของโปรแกรม (หนึ่งในตัวเลือกgoto
) จากนั้นcatch
บล็อกว่างเปล่าในโปรแกรมที่เขียนในภาษานี้จะคล้ายกับelse
บล็อกว่างเปล่าในภาษาดั้งเดิม (หรือไม่มีelse
บล็อกเลย) อย่างไรก็ตามฉันเชื่อว่านี่ไม่ใช่รูปแบบการเขียนโปรแกรมที่แนะนำโดยชุมชนการพัฒนา C #, Java หรือ C ++
ในบางกรณี Java ต้องการให้คุณจัดการกับข้อยกเว้นที่อาจเกิดขึ้นได้ พิจารณารหัสนี้:
try {
bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}
ทุกการใช้งานของแพลตฟอร์ม Java จำเป็นต้องมีการสนับสนุนชุดอักขระมาตรฐานต่อไปนี้
...
UTF-8
หากไม่มีการทำลายแพลตฟอร์ม Java ของคุณก็ไม่มีทางที่จะทำให้เกิดข้อยกเว้นนี้ได้ ดังนั้นทำไมต้องจัดการกับมัน แต่คุณไม่สามารถเลิกลองประโยคได้
throw new Error(e)
เพียงเพื่อให้แน่ใจว่าฉันจะไม่พลาดอะไรเลย (หรือรายงานแพลตฟอร์มที่เสียจริง)
ตามกฎทั่วไปไม่มี คุณต้องการที่จะโยนข้อยกเว้นขึ้นสแต็กหรือบันทึกสิ่งที่เกิดขึ้น
อย่างไรก็ตามฉันมักจะทำเช่นนี้เมื่อฉันแยกวิเคราะห์สตริงและแปลงเป็นตัวเลข (โดยเฉพาะในการทำแผนที่โครงการที่มีการใช้งานครั้งเดียวและทิ้งความหลากหลาย)
boolean isNonZeroNumber = false;
try {
if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
b = true;
}
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}
return b;
ในบางกรณีข้อยกเว้นนั้นไม่พิเศษเลย ในความเป็นจริงก็คาดว่า ลองพิจารณาตัวอย่างนี้:
/* Check if the process is still alive; exitValue() throws an exception if it is */
try {
p.exitValue();
processPool.remove(p);
}
catch (IllegalThreadStateException e) { /* expected behaviour */ }
เนื่องจากไม่มี isAlive () วิธีการใน java.lang.Process () เกี่ยวกับวิธีเดียวที่จะตรวจสอบว่ามันยังมีชีวิตอยู่คือการเรียก exitValue () ซึ่งจะโยน IllegalThreadStateException หากกระบวนการยังคงทำงานอยู่
จากประสบการณ์ที่ต่ำต้อยของฉันสิ่งนี้ไม่ค่อยดี ไมล์สะสมของคุณอาจแตกต่างกัน
เกือบทุกครั้งที่ฉันเห็นสิ่งนี้ในฐานรหัสของเรามันเป็นเพราะฉันได้ติดตามข้อผิดพลาดที่ข้อยกเว้นการกินถูกซ่อนไว้ หากต้องการใส่วิธีอื่นโดยไม่ให้รหัสใด ๆ แอปพลิเคชันไม่มีวิธีระบุข้อผิดพลาดหรือดำเนินการกระทำอื่น
บางครั้งที่เลเยอร์ API คุณอาจไม่ต้องการหรือไม่สามารถให้ข้อยกเว้นผ่านมาดังนั้นในกรณีนี้ฉันมักจะลองและจับคนทั่วไปที่สุดแล้วบันทึกสิ่งเหล่านั้น อีกครั้งอย่างน้อยก็ให้โอกาสในการดีบัก / วินิจฉัย
โดยปกติแล้วถ้ามันจะต้องเป็นที่ว่างเปล่าอย่างน้อยฉันทำคือการใส่การยืนยันของบางชนิดดังนั้นอย่างน้อยสิ่งที่เกิดขึ้นในช่วงการแก้ปัญหา ที่กล่าวว่าถ้าฉันไม่สามารถจัดการได้ฉันมักจะละเว้นพวกเขาอย่างสมบูรณ์
IMO มีที่เดียวที่คำสั่ง catch ว่างเปล่าตกลง ในกรณีทดสอบที่คุณคาดว่าจะมีข้อยกเว้น:
try
{
DoSomething(); //This should throw exception if everything works well
Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
//Expected to get here
}
ฉันเชื่อว่ามีบางกรณีที่มีเหตุผลที่จะมีประโยค catch เปล่า - นี่เป็นกรณีเมื่อคุณต้องการให้โค้ดทำงานต่อไปหากการดำเนินการบางอย่างล้มเหลว อย่างไรก็ตามฉันคิดว่ารูปแบบนี้สามารถใช้งานได้ง่ายเกินไปดังนั้นควรตรวจสอบการใช้แต่ละครั้งอย่างใกล้ชิดเพื่อให้แน่ใจว่าไม่มีวิธีที่ดีกว่าในการจัดการกับมัน โดยเฉพาะอย่างยิ่งสิ่งนี้ไม่ควรกระทำหากออกจากโปรแกรมในสถานะที่ไม่สอดคล้องกันหรืออาจทำให้บางส่วนของรหัสล้มเหลวในภายหลัง
ฉันไม่เชื่อว่าจะไม่มีการแจ้งเตือนในระดับหนึ่งเมื่อมีข้อยกเว้นเกิดขึ้น - หนึ่งในเป้าหมายของการเขียนโปรแกรมไม่ควรปรับปรุงรหัสหรือไม่ หากมีข้อยกเว้นเกิดขึ้น 10% ของเวลาและคุณไม่เคยรู้เลยว่าข้อยกเว้นนั้นจะเป็นเรื่องเลวร้ายหรือแย่กว่านั้นเสมอ
อย่างไรก็ตามฉันเห็นอีกด้านหนึ่งว่าถ้าข้อยกเว้นไม่มีอันตรายใด ๆ ในการประมวลผลของรหัสแล้วทำไมผู้ใช้ถึงเปิดเผยมัน วิธีแก้ปัญหาที่ฉันนำมาใช้มักจะมีการบันทึกโดยคลาส logger ของฉัน (ที่อยู่ในทุกชั้นเดียวที่ฉันเคยเขียนในที่ทำงาน)
หากเป็นข้อยกเว้นที่ใจดีฉันจะโทรไปที่. Logger's () วิธีการ; มิฉะนั้น Logger.Error () (และ (อาจ)) การโยน
try
{
doWork();
}
catch(BenignException x)
{
_log.Debug("Error doing work: " + x.Message);
}
โดยวิธีการในการใช้งานของคนตัดไม้ของฉันการโทรไปที่วิธี. Debug () จะบันทึกเฉพาะถ้าฉัน App.Config ไฟล์ของฉันระบุว่าระดับการบันทึกการดำเนินการของโปรแกรมอยู่ในโหมด Debug
การตรวจสอบการมีอยู่เป็นกรณีการใช้ที่ดี:
// If an exception happens, it doesn't matter. Log the initial value or the new value
function logId(person) {
let id = 'No ID';
try {
id = person.data.id;
} catch {}
console.log(id);
}
อีกกรณีหนึ่งคือเมื่อมีการใช้finally
ข้อ:
function getter(obj){}
function objChecker()
{
try
{
/* Check argument length and constructor type */
if (getter.length === 1 && getter.constructor === Function)
{
console.log("Correct implementation");
return true;
}
else
{
console.log("Wrong implementation");
return false;
}
}
catch(e)
{
}
finally
{
console.log(JSON.stringify(getter))
}
}
// Test the override of the getter method
getter = getter;
objChecker();
getter = [];
objChecker();
getter = {};
objChecker();
getter = RegExp;
objChecker();
getter = Function;
objChecker();
มีข้อเสนอเพื่อให้ละเว้นการจับการจับใน ECMAScript ซึ่งเกี่ยวข้องกับกรณีนี้และการใช้งานที่คล้ายกัน
อ้างอิง
ครั้งเดียวที่ฉันต้องการทำบางสิ่งเช่นนี้กับคลาสการบันทึกสำหรับแอปคอนโซล มีตัวจัดการการดักจับโกลบอลระดับบนสุดซึ่งส่งข้อผิดพลาดไปยัง STDOUT บันทึกข้อผิดพลาดไปยังไฟล์จากนั้นปิดแอปด้วยรหัสข้อผิดพลาด> 0
ปัญหาที่เกิดขึ้นคือบางครั้งการบันทึกไฟล์ล้มเหลว สิทธิ์ของไดเรกทอรีทำให้คุณไม่สามารถเขียนไฟล์ได้ไฟล์ดังกล่าวถูกล็อคไว้โดยเฉพาะ หากสิ่งนั้นเกิดขึ้นฉันไม่สามารถจัดการข้อยกเว้นได้ในลักษณะเดียวกับที่ฉันมี (ที่อื่นบันทึกการบันทึกรายละเอียดที่เกี่ยวข้องห่อในประเภทข้อยกเว้นที่ดีกว่า ฯลฯ ) เนื่องจากการบันทึกรายละเอียดข้อยกเว้นทำให้คนตัดไม้ทำงานแบบเดียวกัน พยายามเขียนไฟล์) ซึ่งล้มเหลวแล้ว เมื่อพวกเขาล้มเหลวอีกครั้ง ... คุณจะเห็นว่าฉันกำลังจะไปไหน มันเข้าสู่วงวนไม่ จำกัด
หากคุณวางบล็อกลอง {} บางส่วนคุณสามารถจบลงด้วยข้อยกเว้นที่ปรากฏในกล่องข้อความ (ไม่ใช่สิ่งที่คุณต้องการสำหรับแอพ config แบบเงียบ) ดังนั้นในวิธีที่เขียนไปยังล็อกไฟล์นั้นฉันมีตัวจัดการ catch ว่างเปล่า การทำเช่นนี้ไม่ได้เป็นการฝังข้อผิดพลาดเนื่องจากคุณยังได้รับรหัสข้อผิดพลาด> 0 แต่มันจะหยุดการวนซ้ำไม่สิ้นสุดและอนุญาตให้แอปออกไปอย่างสง่างาม
มีความคิดเห็นแน่นอนอธิบายว่าทำไมมันว่างเปล่า
(ฉันจะโพสต์ก่อนหน้านี้ฉันแค่กลัวว่าจะโดนเผาจนลืมเลือนเพราะฉันไม่ใช่คนเดียว แต่ ... )
มันก็โอเคในสถานการณ์ที่ต้องดำเนินการบางอย่างไม่ว่าอะไรจะเกิดขึ้นกับชิ้นส่วนที่สำคัญที่สุดในตอนท้าย
ตัวอย่างเช่น. ในการสื่อสารการควบคุมการเคลื่อนไหวของโฮสต์กับตัวควบคุม ในสถานการณ์ฉุกเฉินมีขั้นตอนการเตรียมการจำนวนหนึ่งเพื่อยกเลิกการเคลื่อนไหวหยุดการสร้างเส้นทางลดความเร็วมอเตอร์เปิดใช้งานการหยุดพักปิดการใช้งานเครื่องขยายเสียงและหลังจากนี้คุณจะต้องฆ่าพลังรถบัส ทั้งหมดจะต้องเกิดขึ้นตามลำดับและหากขั้นตอนใดขั้นตอนหนึ่งล้มเหลวต้องดำเนินการขั้นตอนที่เหลือต่อไปแม้ว่าจะมีความเสี่ยงที่จะเกิดความเสียหายกับฮาร์ดแวร์ พูดแม้ว่าข้างต้นล้มเหลวพลังรถบัสฆ่าจะต้องเกิดขึ้นต่อไป
การปิดทรัพยากรระบบอย่างสง่างามเพื่อกู้คืนจากข้อผิดพลาด
ตัวอย่างเช่น หากคุณกำลังใช้บริการในพื้นหลังที่ไม่ได้ให้ข้อเสนอแนะให้กับผู้ใช้ที่คุณอาจไม่ต้องการที่จะผลักดันข้อบ่งชี้ของข้อผิดพลาดไปยังผู้ใช้ แต่คุณไม่จำเป็นต้องปิดจากทรัพยากรที่ถูกนำมาใช้ในลักษณะที่สง่างาม
try
{
// execution code here
}
catch(Exception e)
{
// do nothing here
}
finally
{
// close db connection
// close file io resource
// close memory stream
// etc...
}
โดยอุดมคติแล้วคุณจะต้องใช้โหมด catch in debug เพื่อตรวจจับข้อผิดพลาดและพิมพ์ลงในคอนโซลหรือไปยังไฟล์บันทึกสำหรับการทดสอบ ในสภาพแวดล้อมการผลิตบริการพื้นหลังโดยทั่วไปคาดว่าจะไม่ขัดจังหวะผู้ใช้เมื่อมีข้อผิดพลาดจะถูกโยนดังนั้นข้อผิดพลาดออกควรจะระงับ