การจัดการ Exceptions เงื่อนไขข้อผิดพลาด ในภาษา Dart เบื้องต้น

19 November 2019 By Ninenik Narkdee
try...catch catch finally rethrow dart throw exceptions

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ try...catch catch finally rethrow dart throw exceptions



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

Exceptions คืออะไร

    Exceptions คือ เงื่อนไขข้อผิดพลาด หรือข้อยกเว้น ในกรณีที่เราต้องการกำหนดการทำงานเพิ่มเติม ให้กับเงื่อนไขนั้นๆ โดยใน
ภาษา Dart จะมีชนิดข้อมูลข้อผิดพลาดมาให้ คือ Exception และ Error  โดยเราสามารถกำหนด Exception ขึ้นมาใช้งานเองโดย
โดยทำการ implement หรือใช้งาน Exception Class ตัวอย่างเช่น
 
 // กำหนด Exception เอง โดยใช้ Exception class
class MyException implements Exception {
  final String msg;

  const MyException([this.msg]);

  @override
  String toString() => msg ?? 'MyException';
}
    ทำความเข้าใจระหว่าง Errors และ Exceptions จะได้ว่า  Exceptions เป็นการพิจารณาเงื่อนไขที่เราสามารถวางแผนการจัดการ
ให้กับสิ่งที่จะเกิดขึ้นได้ ส่วน Errors เป็นเงื่อนไข ข้อผิดพลาดที่เราคาดเดาไม่ได้ หรือไม่ได้วางแผนจัดการเอาไว้
    อย่างไรก็ตามในภาษา Dart นอกจาก Exception และ Error object แล้ว ยังสามารถเกิด Exception ที่เป้นชนิดข้อมูลอื่นๆ ที่ไม่ใช่ 
NULL object ได้อีกด้วย ตัวอย่างเช่น เป็นข้อความ String
 
// สร้าง Exceptions ที่เป้น String ข้อความด้วย "throw" keyword
throw 'ยอดเงินไม่เพียงพอ';
 
    ตอนนี้เราเข้าใจเกี่ยวกับ Exceptions คร่าวๆ แล้ว ต่อไปเราจะมาดูวิธีการกำหนด และใช้งาน Exceptions รวมถึงการใช้งาน throw, on, 
catch, rethrow และ finally กันต่อตามลำดับดังนี้
 
 
 

การกำหนด Exceptions

    การใช้งาน Throw

    อย่างที่เราทราบมาบ้างแล้วว่า การตรวจจับ Exception สามารถจัดการโดยการใช้งาน try...catch ดูตัวอย่างประกอบ
 
void main() {
  try {
    double deposit = 200;  // เงินเกิบ
    double withdraw = 300;  // จะถอนมาใช้
    
    if(withdraw > deposit){ // ถอนเงินเกินยอดที่มี 
      throw 'ยอดเงินไม่เพียงพอ'; // สร้าง เงื่อนไขข้อผิพลาด
    }   
  } catch (e) {
    // ได้ Exceptions เป็นชนิดข้อมูล String
    print('Exceptions ที่มีชนิดข้อมูลเป็น ${e.runtimeType}.');
    // output: Exceptions ที่มีชนิดข้อมูลเป็น String.
  }
}
    ตัวอย่างการใช้งานกับ Exceptions ที่กำหนดเอง และใช้งาน exception class
 
void main() {
  try {
    double deposit = 200;  // เงินเกิบ
    double withdraw = 300;  // จะถอนมาใช้
    
    if(withdraw > deposit){ // ถอนเงินเกินยอดที่มี 
      throw WithdrawException('ยอดเงินไม่เพียงพอ'); // สร้าง เงื่อนไขข้อผิพลาด
    }   
  } catch (e) {
    print(e); // output: ยอดเงินไม่เพียงพอ
    print('Exceptions ที่มีชนิดข้อมูลเป็น ${e.runtimeType}.');
    // output: Exceptions ที่มีชนิดข้อมูลเป็น WithdrawException.
  }
}

 // กำหนด Exception เอง โดยใช้ Exception class
class WithdrawException implements Exception {
  final String msg;

  const WithdrawException([this.msg]);

  @override
  String toString() => msg ?? 'ยอดเงินไม่เพียงพอ';
}
 
    จะเห็นว่าเราใช้ "throw" ในการกำหนด Exceptions ซึ่งในการใช้งาน ถึงแม้เราจะสามารถกำหนดชนิดข้อมูลอื่นๆ อย่าง String ให้กับ
Exceptions แต่เพื่อใช้งานที่มีประสิทธิภาพ การกำหนด Exceptions โดยใช้งาน Exception หรือ Error class จะเป็นวิธีที่ดีกว่า เหมือนใน
กรณีการใช้งาน WithdrawException ในตัวอย่างที่สองข้างต้น ที่มีการ implement มาจาก Exception class
    ตัวอย่างการใช้งานกับ Exception class โดยตรง
 
void main() {
  try {
    double deposit = 200;  // เงินเกิบ
    double withdraw = 300;  // จะถอนมาใช้
    
    if(withdraw > deposit){ // ถอนเงินเกินยอดที่มี 
      throw Exception('ยอดเงินไม่เพียงพอ'); // สร้าง เงื่อนไขข้อผิพลาด
    }   
  } catch (e) {
    print(e); // output: ยอดเงินไม่เพียงพอ
    print('Exceptions ที่มีชนิดข้อมูลเป็น ${e.runtimeType}.');
    // output: Exceptions ที่มีชนิดข้อมูลเป็น _Exception.
  }
}
 
    ในกรณีใช้ Error class โดยตรง สามารถใช้เป็น throw Error(); 
    ผลลัพธ์ที่ได้ จะเป็น Exceptions ที่มีชนิดข้อมูลเป็น Error.

 
 

    การใช้งาน Catch และ On

    การตรวจจับเงื่อนไขพิเศษ หรือ Exception ที่ถูกส่งออกมาโดยการใช้งาน "throw" keyword ก็เพื่อช่วยให้เราสามารถจัดการกับเงื่อนไขนั้น
หรือกำหนดการทำงาน ให้กับเงื่อนไขข้อผิดพลาดที่เกิดขึ้น  ในตัวอย่างด้านบน เราจะเห็นว่า มีการใช้ "catch" ในการตรวจจับ exception ซึ่งจะ
ไม่มีการระบุชนิดของ Exception หรือก็คือ เมื่อเราใช้ "catch" ก็จะเป็นการทุกๆ exception ที่เกิดขึ้นทั้งหมด
    อย่างไรก็ตาม เราสามารถใช้ "on" keyword แทน หรือใช้งานร่วมกับ "catch" keyword ก็ได้ ในกรณีที่เราต้องการตรวจสอบเงื่อนไข โดย
มีการพิจารณาชนิดของ Exceptions ด้วย  ดูตัวอย่างโค้ดด้านล่างประกอบ
 
 
void main() {
  try {
    double deposit = 200;  // เงินเกิบ
    double withdraw = 300;  // จะถอนมาใช้
    
    if(withdraw > deposit){ // ถอนเงินเกินยอดที่มี 
      throw WithdrawException('ยอดเงินไม่เพียงพอ');
    }   
  } on WithdrawException catch (e) {
    print(e); // output: ยอดเงินไม่เพียงพอ
    print('Exceptions ที่มีชนิดข้อมูลเป็น ${e.runtimeType}.');
  }
}

 // กำหนด Exception เอง โดยใช้ Exception class
class WithdrawException implements Exception {
  final String msg;

  const WithdrawException([this.msg]);

  @override
  String toString() => msg ?? 'ยอดเงินไม่เพียงพอ';
}
 
    จากตัวอย่างข้างต้น เราตรวจสอบ Exceptions ที่เป้น WithdrawException ซึ่งมีการคืนค่า error ข้อความกลับมา เราต้องใช้ catch (e) หาก
ต้องการนำค่าไปใช้งานต่อ
 
    เรามาลองเพิ่ม การกำหนด  Exceptions หลายๆ เงื่อนไข เป็นดังนี้
 
void main() {
  try {
    double deposit = 200;  // เงินเกิบ
    double withdraw = 0;  // จะถอนมาใช้
    
    if(withdraw > deposit){ // ถอนเงินเกินยอดที่มี 
      throw WithdrawException('ยอดเงินไม่เพียงพอ');
    }else if(withdraw == 0){
      throw Exception("ยอดเงินที่ต้องการถอน ต้องมากกว่า 0");
    }else if(withdraw < 0){
      throw "ยอดเงินที่ต้องการถอน ไม่ถูกต้อง";
    }   
  } on WithdrawException {//  ถ้า withdraw มากกว่า 200 เช่น 300
    // กรณีเราไม่ต้องการใช้งานค่า erro ก็ไม่ต้องกำหนด catch (e)
    print('Exceptions เป็นชนิด WithdrawException');    
  } on Exception catch (e) { // ถ้า withdraw เท่ากับ 0 
    print(e); // output: ยอดเงินที่ต้องการถอน ต้องมากกว่า 0
    print('Exceptions ที่มีชนิดข้อมูลเป็น ${e.runtimeType}.');
    // Exceptions ที่มีชนิดข้อมูลเป็น _Exception.
  } catch (e) { // ถ้า withdraw เป็นตัวเลขติดลบ เช่น -200
    print(e); // output: ยอดเงินที่ต้องการถอน ไม่ถูกต้อง
    print('Exceptions ที่มีชนิดข้อมูลเป็น ${e.runtimeType}.');    
    // Exceptions ที่มีชนิดข้อมูลเป็น String.
  }
}

 // กำหนด Exception เอง โดยใช้ Exception class
class WithdrawException implements Exception {
  final String msg;

  const WithdrawException([this.msg]);

  @override
  String toString() => msg ?? 'ยอดเงินไม่เพียงพอ';
}
 
    จากตัวอย่าง จะเห็นว่า เรากำหนด รูปแบบของ Exceptions ที่หลากหลาย ไม่ว่าจะเป็น WithdrawException , Exception และ String โดย
ในการตรวจจับ Exceptions เราใช้ "on" กับกรณีที่ต้องการระบุว่าเป็นข้อมูล ชนิดใด เป็น พิเศษ และใช้ "on" กับ "catch (e)" กรณีที่ต้องการระบุ
ชนิดข้อมูล และใช้งานค่าที่ถูกส่งกลับมา และสุดท้าย เราใช้งาน "catch" สำหรับ Exceptions กรณีอื่นๆ ที่ไม่เข้าเงื่อนไขก่อนหน้า
    ในการใช้งาน "catch" เราสามารถกำหนด parameter 1 ค่า เช่น catch (e) หรือ สองค่า เช่น catch (e, s) โดยที่ e คือ exception object
ที่ถูก throw ออกมา ส่วน s คือ stack trace object เป็นลำดับข้อมูลการทำงานการเกิด exception
    ตัวอย่างโค้ดการใช้งานบางส่วน
 
  } catch (e, s) { // ถ้า withdraw เป็นตัวเลขติดลบ เช่น -200
    print(e); // output: ยอดเงินที่ต้องการถอน ไม่ถูกต้อง
    print('Exceptions ที่มีชนิดข้อมูลเป็น ${e.runtimeType}.');    
    // Exceptions ที่มีชนิดข้อมูลเป็น String.
    
    print('Stack trace:\n $s');
    print('StackTrace ที่มีชนิดข้อมูลเป็น ${s.runtimeType}.');    
  }
 
 

    การใช้งาน Rethrow

    ในกรณีที่เราต้องการตรวจสอบ Exception เป็นส่วนๆ หรือบางส่วน เช่น สมมติเราสร้างฟังก์ชั่นในการตรวจสอบข้อมูลขึ้นมา และทำการเรียกใช้
งานฟังก์ชั้นนั้น พร้อมกับตรวจจับ Exceptions ด้วย try...catch อีกที เราสามารถส่งต่อ exception object ไปยังการจัดการหลัก โดยใช้ "rethrow"
keyword เพื่้อส่งต่อ Exceptions ได้ ดูตัวอย่างโค้ดด้านล่างประกอบ
 
void main() {
    double deposit = 200;  // เงินเกิบ
    double withdraw = 0;  // จะถอนมาใช้
  
    try {
      // ใช้งานฟังก์ชั่นตรวจสอบข้อมูล
      validWithdraw(deposit, withdraw);
    } catch (e){
      // Exception ส่วนนี้มาจาก การ rethrow มาอีกที
      print(e); // output: ยอดเงินที่ต้องการถอน ไม่ถูกต้อง
      print('Exception ใน main() ฟังก์ชั่น ${e.runtimeType}.');         
    }
}

// สร้างฟังก์ชั่น ตรวจสอบข้อมูล
void validWithdraw(double deposit ,double withdraw){
  try {
    if(withdraw > deposit){ // ถอนเงินเกินยอดที่มี 
      throw WithdrawException('ยอดเงินไม่เพียงพอ');
    }else if(withdraw == 0){
      throw Exception("ยอดเงินที่ต้องการถอน ต้องมากกว่า 0");
    }else if(withdraw < 0){
      throw "ยอดเงินที่ต้องการถอน ไม่ถูกต้อง";
    }   
  } on WithdrawException {//  ถ้า withdraw มากกว่า 200 เช่น 300
    // กรณีเราไม่ต้องการใช้งานค่า erro ก็ไม่ต้องกำหนด catch (e)
    print('Exceptions เป็นชนิด WithdrawException');    
  } on Exception catch (e) { // ถ้า withdraw เท่ากับ 0 
    print(e); // output: ยอดเงินที่ต้องการถอน ต้องมากกว่า 0
    print('Exception ใน validWithdraw() ฟังก์ชั่น ${e.runtimeType}.'); 
    // Exceptions ที่มีชนิดข้อมูลเป็น _Exception.
    rethrow; // ส่งต่อ Exception object 
  } catch (e) { // ถ้า withdraw เป็นตัวเลขติดลบ เช่น -200
    print(e); // output: ยอดเงินที่ต้องการถอน ไม่ถูกต้อง
    print('Exception ใน validWithdraw() ฟังก์ชั่น ${e.runtimeType}.'); 
    rethrow; // ส่งต่อ Exception object 
  }  
}

 // กำหนด Exception เอง โดยใช้ Exception class
class WithdrawException implements Exception {
  final String msg;

  const WithdrawException([this.msg]);

  @override
  String toString() => msg ?? 'ยอดเงินไม่เพียงพอ';
}
    ในตัวอย่าง เรากำหนด rethrow ให้กับกรณีที่ ยอดเงินที่ถอนเท่ากับ 0 และ กรณีที่เป็นเลขติดลบ ทำให้ เมื่อมีการปล่อย Exception ออกมาจาก
ฟังก์ชั่น validWithdraw() ก็จะถูกส่งไปใช้ต่อไปอีกที และถูกตรวจจับในฟังก์ชั่น main() ผลลัพธ์ที่ได้ กรณี กำหนดค่า withdraw เท่ากับ 0 จะได้เป็น
 
Exception: ยอดเงินที่ต้องการถอน ต้องมากกว่า 0
Exception ใน validWithdraw() ฟังก์ชั่น _Exception.
Exception: ยอดเงินที่ต้องการถอน ต้องมากกว่า 0
Exception ใน main() ฟังก์ชั่น _Exception.
 
 

    การใช้งาน Finally

    ในกรณีที่เราต้องการทำคำสั่งบางอย่าง โดยไม่สนใจว่าจะเกิด Exceptions ขึ้นหรือไม่ เราสามารถใช้งาน "finally keyword ร่วมกับ try หรือก็คือ
try....finally  โดย finally ก็เหมือนเงื่อนไขสุดท้ายที่อยากให้มี หรืออยากให้ทำงานเสมอ  โดยจะมี Exceptions หรือไม่ก็ตาม  เราจะลองใช้โค้ด
จากด้านบนมาอธิบาย บางส่วนดังนี้
 
void main() {
    double deposit = 200;  // เงินเกิบ
    double withdraw = 200;  // จะถอนมาใช้
  
    try {
      // ใช้งานฟังก์ชั่นตรวจสอบข้อมูล
      validWithdraw(deposit, withdraw);
    } finally {
      print('Finally');         
    }
}
 
    ในตัวอย่างนี้ เราถอนเงิน 200 บาท จากเงินที่มีทั้งหมด 200 ซึ่งกือสามารถทำได้ปกติ และไม่เกิด Exceptions ขึ้น และเราต้องการให้พิมพ์คำว่า 
"Finally" ทุกๆ ครั้งที่มีการถอนเงิน เราจึงใช้งาน "finally" keyword ตามตัวอย่าง ผลลัพธ์ที่ได้ ก็คือ จะแสดงข้อความว่า "Finally" ออกมา 
    ทีนี้ดูต่อว่า เราสนใจหากเกิด Exceptions ขึ้น ต้องการรับค่า Exception ที่ถูกส่งต่อมาถ้ามี โดยใช้รูปแบบ try...catch..finally คือ จะมีหรือไม่มี
Exceptions ก็ต้องทำการพิมพ์ข้อความ "Finally" เสมอ ก็จะได้เป็น
 
void main() {
    double deposit = 200;  // เงินเกิบ
    double withdraw = 0;  // จะถอนมาใช้
  
    try {
      // ใช้งานฟังก์ชั่นตรวจสอบข้อมูล
      validWithdraw(deposit, withdraw);
    } catch (e){
      // Exception ส่วนนี้มาจาก การ rethrow มาอีกที
      print(e); // output: ยอดเงินที่ต้องการถอน ไม่ถูกต้อง
      print('Exception ใน main() ฟังก์ชั่น ${e.runtimeType}.');           
    } finally {
      print('Finally');         
    }
}
    ในตัวอย่าง เราต้องการถอนเงิน 0 บาท ซึ่งเป็นจำนวนเงินที่ทำให้เกิด Exceptions ของเงื่อนไขยอดเงินต้องมากกว่า 0 ซึ่งเป็น Exceptions ที่
เกิดในฟังก์ชั่น validWithdraw() และถูกส่งต่อด้วย "rethrow" keyword  เมื่อ main() ฟังก์ชั่นเรียกใช้งาน ก็จะแสดงผลลัพธ์นั้นซ้ำอีกที และจบด้วย
finally ทำงานคำสั่งสุดท้ายที่ต้องการ คือพิมพ์ข้อความ "Finally" ออกมา
 
    ลำดับผลลัพธ์การทำงานที่เกิดขึ้น จะเห็นว่า การใช้งาน "finally" keyword ก็เพื่อให้สามารถใช้งานหรือทำคำสั่งที่ต้องการเสมอ แม้จะมีหรือไม่
มี Exceptions เกิดขึ้นก็ตาม ปกติ มักประยุกต์ใช้ในการล้างค่า หรือ รีเซ็ตค่าที่ไม่ได้ใช้งานแล้ว
 
Exception: ยอดเงินที่ต้องการถอน ต้องมากกว่า 0
Exception ใน validWithdraw() ฟังก์ชั่น _Exception.
Exception: ยอดเงินที่ต้องการถอน ต้องมากกว่า 0
Exception ใน main() ฟังก์ชั่น _Exception.
Finally
 
 
    หวังว่าเนื้อหานี้ จะเป็นประโยชน์และแนวทาง ในการนำไปใช้งานและทำความเข้าใจต่อไป


เพียงกด Like หรือ Share เป็นกำลังใจ ในการสร้างบทความใหม่ๆ น่ะครับ



อ่านต่อที่บทความ









เนื้อหาที่เกี่ยวข้อง



Tags:: finally rethrow exceptions catch dart try...catch throw







URL สำหรับอ้างอิง











เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ