28.Exception handler

💬 เคยไหมโปรแกรมที่เราเขียนพอทำงานอยู่ดีๆก็เด้งปิดโปรแกรมไป หรือไม่ก็เจอข้อความแปลกๆเต็มจอไปหมดแล้วโปรแกรมก็เด้งไป ถ้าเจอปัญหาพวกนั้นแสดงว่าในโค้ดของเรามันมี error เกิดขึ้น ซึ่งเราจะต้องจัดการกับ error เหล่านั้นไม่ให้โปรแกรมเด้งออก ซึ่งตัวที่จะมาช่วยเราจัดการ error นั่นก็คือสิ่งที่เรียกว่า Exception handler นั่นเอง

🎯 สรุปสั้นๆ

👨‍🚀 Exception handler คือ

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

try
{
   // โค้ดที่มีความเสี่ยงว่าอาจจะเกิด error ขึ้นได้
}
catch (Exception e)
{
   // ถ้าเกิด error ขึ้นภายในบรรทัดที่ 2~4 มันจะกระโดดมาทำภายใน block นี้
}

👨‍🚀 การดักจับ exception แบบระบุ

ตอนที่เกิด error ขึ้น เราสามารถเลือกได้ว่า "ถ้าเกิด error แบบ A เราจะจัดการแบบนี้ หรือ "ถ้าเกิด error แบบ B ให้จัดการแบบโน้น" ก็ได้เหมือนกันนะ โดยการระบุประเภทของ exception ลงไปใน catch ตามตัวอย่างด้านล่าง

try
{
   // โค้ดที่มีความเสี่ยงว่าอาจจะเกิด error ขึ้นได้
}
catch (DivideByZeroException e)
{
   // ถ้าเกิด error ขึ้นภายในบรรทัดที่ 2~4 
   // และเป็น error จากการที่ตัวหารเป็น 0 มันจะกระโดดมาทำภายใน block นี้
}
catch (NullReferenceException e)
{
   // ถ้าเกิด error ขึ้นภายในบรรทัดที่ 2~4
   // และเป็น error จากการที่เราใช้ตัวแปรที่เป็น null มันจะกระโดดมาทำภายใน block นี้
}
catch (Exception e)
{
   // ถ้าเกิด error ขึ้นภายในบรรทัดที่ 2~4
   // และ error ที่เกิดขึ้นไม่ใช่เกิดจาก ตัวหารเป็น 0 หรือ เรียกใช้ตัวแปรที่เป็น null
   // มันจะกระโดดมาทำภายใน block นี้
}

Exception คือต้นตระกูลของ exception ทุกตัว ดังนั้นถ้าเกิด error ที่เกิดขึ้น ไม่ตรงกับ catch ตัวไหนเลย มันจะเข้าไปที่ Exception

Exception ถ้ามี catch หลายๆตัว ห้ามเอา Exception ไปไว้ใน catch ตัวแรก เพราะถ้ามันเกิด error ขึ้นมันจะเข้า catch(Exception e) ตัวแรกที่วางไว้โดยไม่สนใจ catch อื่นๆด้านล่างเลย

👨‍🚀 Finally keyword

คือ block ที่ระบุว่า ไม่ว่าจะทำแล้วเกิด error ขึ้นหรือไม่ หลังจากทำ try หรือ catch เสร็จ มันก็จะมาทำโค้ดใน block ของ finally เป็นลำดับถัดไปนั่นเอง

try
{
   // โค้ดที่มีความเสี่ยงว่าอาจจะเกิด error ขึ้นได้
}
catch (Exception e)
{
   // ถ้าเกิด error ขึ้นภายในบรรทัดที่ 2~4 มันจะกระโดดมาทำภายใน block นี้
}
finally
{
   // เมื่อทำ try หรือ catch เสร็จ มันจะกระโดดมาทำภายใน block นี้เสมอ
}

👨‍🚀 throw keyword

คือคำสั่งที่บอกว่า ณ โค้ดจุดนั้นจะไม่ขอจัดการ error นี้แล้ว และจะทำการส่ง error นั้นๆไปให้กับ โค้ดที่เรียกมันรับหน้าที่จัดการต่อ

try
{
   // โค้ดที่มีความเสี่ยงว่าอาจจะเกิด error ขึ้นได้
}
catch (Exception e)
{
   throw e;
}

หรืออยู่ๆ เราอยากจะโยน error ออกจาก method ดื้นๆทั้งๆที่ยังไม่เกิด error ก็ได้ เช่น ผมเขียน method ที่รับชื่อเข้ามา ซึ่งถ้าส่งชื่อเข้ามามันจะทักทาย แต่ถ้าส่ง null หรือช่องว่างเข้ามามันจะส่ง error ออกไปแทน เราก็จะได้โคีดตามตัวอย่างด้านล่างนี้ครับ

public void Greeting(string name)
{
    if (string.IsNullOrWhiteSpace(name))
    {
        throw new ArgumentNullException();
    }

    Console.Write("Greeting " + name);
}

👨‍🚀 การส่งต่อ Exception โดยมี message

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

throw new ArgumentNullException("name", "กรุณากำหนดชื่อเข้ามาด้วย");

Exception message เวลาที่เราใส่ message ให้กับ exception object ลองกดดู overload ของมันด้วยนะ เพราะมันมี overload ให้เราเลือกเยอะม๊วก

👨‍🚀 Custom Exception

นอกเหนือจาก exception มาตรฐานที่ Microsoft เตรียมไว้ให้แล้ว ถ้าเรามี exception แบบพิเศษในแบบของเราเอง เราก็สามารถทำได้เหมือนกัน โดยการสร้างคลาสธรรมดานี่แหละไปสืบทอดจากพวก exception class

public class MyCustomException : Exception
{
}

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

👨‍🚀 ข้อควรระวังในการใช้ try/catch

โดยปรกติการใช้ try/catch มันจะทำให้ความเร็วในการทำงานตกลง ดังนั้นในทางปฏิบัติจริงๆเราควรจะหลีกเลี่ยงการใช้ try/catch ให้ได้มากที่สุดเท่าที่จะทำได้ เช่นการตรวจสอบก่อนว่าค่านั้นๆเป็น null หรือเปล่า หรือตัวหารเป็นศูนย์หรือเปล่า ก่อนที่จะนำตัวแปรพวกนั้นไป execute จริงๆนะครับ ตามตัวอย่างโค้ดด้านล่างเลยเป็นการป้องกันตัวหารเป็นศูนย์

public void Divide(double dividend, double divisor)
{
   if(divisor == 0)
   {
      Console.WriteLine("ตัวหารไม่สามารถเป็นศูนย์ได้นะ!")
   }
   else
   {
      Console.WriteLine(dividend / divisor);
   }
}

Last updated