การบังคับเรียกใช้ฟังก์ชันที่จำเป็นใน C#
ปัญหา: การเรียกฟังก์ชันที่ไม่ได้รับการตรวจสอบ
ใน C# การสร้างฟังก์ชันที่คืนค่าความสถานะเป็นเรื่องที่ค่อนข้างธรรมดา ช่วยให้นักพัฒนาติดตามความสำเร็จหรือความล้มเหลวของการทำงาน อย่างไรก็ตาม ข้อผิดพลาดทั่วไปคือบางครั้งนักพัฒนาบางคนอาจมองข้ามสถานะที่คืนค่านี้โดยสิ้นเชิง ซึ่งอาจนำไปสู่ผลกระทบที่ไม่ได้ตั้งใจหากไม่มีการจัดการข้อผิดพลาดอย่างเหมาะสม
ยกตัวอย่างเช่น พิจารณาคลาส Status
ซึ่งอาจมีข้อมูลเกี่ยวกับว่าการทำงานนั้นประสบความสำเร็จหรือมีปัญหาในระหว่างการดำเนินการหรือไม่:
Status MyFunction()
{
if(...) // อย่างใดอย่างหนึ่งไม่ดี
return new Status(false, "เกิดข้อผิดพลาดบางอย่าง");
else
return new Status(true, "โอเค");
}
ความคาดหวังคือผู้เรียกทุกคนของ MyFunction
จะต้องตรวจสอบสถานะที่คืนค่า:
Status myStatus = MyFunction();
if (!myStatus.IsOK())
// จัดการมัน แสดงข้อความ,...
อย่างไรก็ตาม บางครั้งผู้เรียกอาจจะขี้เกียจละเลยการตรวจสอบสถานะ:
// มองข้ามสถานะที่คืนค่า
MyFunction(); // เรียกฟังก์ชันและมองข้ามสถานะที่คืนค่า
สามารถป้องกันสิ่งนี้ได้ไหม?
นี่ทำให้เกิดคำถามที่สำคัญ: เราจะรับประกันได้อย่างไรว่า ทุกผู้เรียกต้องตรวจสอบสถานะที่คืนค่า? ใน C++ สามารถใช้ดีสทรัคเตอร์เพื่อตรวจสอบสถานะที่จุดสิ้นสุดของวัฏจักรชีวิตของวัตถุ แต่ใน C# ดีสทรัคเตอร์โดยทั่วไปจะไม่ได้รับการชื่นชมเนื่องจากธรรมชาติที่ไม่สามารถคาดการณ์ได้ของเครื่องเก็บขยะ
โซลูชัน: การใช้ดีเลเกตเพื่อการเรียกฟังก์ชันที่จำเป็น
แม้ว่า C# จะขาดความสามารถในการบังคับให้ค่าคืนจากเมธอดถูกเรียกใช้หรือถูกตรวจสอบ แต่การทำงานรอบด้านนวัตกรรมเกี่ยวข้องกับการใช้ ดีเลเกต เทคนิคนี้ไม่เพียงแต่ส่งค่ากลับอย่างเดียว แต่ยังบังคับให้ผู้เรียกต้องประมวลผลสถานะที่คืนค่าผ่านกลไกการเรียกกลับอย่างชัดเจน
การดำเนินการตัวอย่าง
มาดูการดำเนินการง่ายๆ โดยใช้ดีเลเกต:
using System;
public class Example
{
public class Toy
{
private bool inCupboard = false;
public void Play() { Console.WriteLine("กำลังเล่น."); }
public void PutAway() { inCupboard = true; }
public bool IsInCupboard { get { return inCupboard; } }
}
public delegate void ToyUseCallback(Toy toy);
public class Parent
{
public static void RequestToy(ToyUseCallback callback)
{
Toy toy = new Toy();
callback(toy);
if (!toy.IsInCupboard)
{
throw new Exception("คุณไม่ได้เก็บของเล่นของคุณไว้ในตู้!");
}
}
}
public class Child
{
public static void Play()
{
Parent.RequestToy(delegate(Toy toy)
{
toy.Play();
// อุ๊บส์! ลืมเก็บของเล่น!
});
}
}
public static void Main()
{
Child.Play();
Console.ReadLine();
}
}
คำอธิบายของโค้ด
ในตัวอย่างนี้ เมื่อตั้ง Parent.RequestToy
ถูกเรียก มันคาดหวังฟังก์ชันการเรียกกลับที่จะดำเนินการกับวัตถุ Toy
หลังจากดำเนินการฟังก์ชันเรียกกลับแล้ว ระบบจะตรวจสอบว่าของเล่นได้ถูกเก็บหรือไม่:
- หากฟังก์ชันเรียกกลับไม่ได้เรียก
toy.PutAway()
จะมีการโยนข้อยกเว้น นี้ช่วยให้ผู้เรียกต้องจัดการกับToy
เมื่อสิ้นสุดการใช้งาน ซึ่งช่วยบังคับให้ดำเนินการตามที่จำเป็น - วิธีนี้ไม่ได้รับประกันว่าผู้เรียกจะจำได้ว่าสถานะนั้นจำเป็นต้องตรวจสอบ แต่บังคับให้มีโครงสร้างในลักษณะที่ยกระดับความสำคัญของการดำเนินการนั้น
บทสรุป
ถึงแม้ว่าใน C# จะไม่สามารถบังคับให้มีการตรวจสอบค่าที่คืนค่าได้อย่างเข้มงวดเหมือนใน C++ แต่เราสามารถใช้ดีเลเกตเพื่อสร้างรูปแบบที่ทำให้สื่อสารความจำเป็นของการดำเนินการถัดไปได้อย่างมีประสิทธิภาพ ทำให้โค้ดของเรามีความน่าเชื่อถือมากขึ้น การใช้รูปแบบนี้จะช่วยระบุการจัดการผลลัพธ์ของฟังก์ชันอย่างเหมาะสมในขณะที่ยังคงรักษาวิธีการเขียนโปรแกรมที่สะอาดและเป็นระเบียบ
โดยการใช้วิธีการนี้ เราช่วยรับประกันว่านักพัฒนาที่ใช้โค้ดของคุณจะไม่สามารถข้ามการตรวจสอบที่จำเป็นได้อย่างง่ายดาย ขณะที่มันอาจดูเป็นวิธีการที่ไม่ค่อยเป็นที่นิยม แต่มันเป็นวิธีที่มีประโยชน์ในการสร้างวัฒนธรรมของความรับผิดชอบในการจัดการข้อผิดพลาด