การบังคับเรียกใช้ฟังก์ชันที่จำเป็นใน 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++ แต่เราสามารถใช้ดีเลเกตเพื่อสร้างรูปแบบที่ทำให้สื่อสารความจำเป็นของการดำเนินการถัดไปได้อย่างมีประสิทธิภาพ ทำให้โค้ดของเรามีความน่าเชื่อถือมากขึ้น การใช้รูปแบบนี้จะช่วยระบุการจัดการผลลัพธ์ของฟังก์ชันอย่างเหมาะสมในขณะที่ยังคงรักษาวิธีการเขียนโปรแกรมที่สะอาดและเป็นระเบียบ

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