ใช้วิธีการขยายสําหรับคลาส

เสร็จสมบูรณ์เมื่อ

โดยทั่วไปแล้ว มีสองวิธีในการเพิ่มวิธีการ ให้กับชนิดที่มีอยู่:

  • ปรับเปลี่ยนรหัสต้นทางสําหรับชนิดนั้น การปรับเปลี่ยนแหล่งข้อมูลจะสร้างการเปลี่ยนแปลงแบบทําลายหากคุณเพิ่มเขตข้อมูลส่วนตัวใด ๆ เพื่อสนับสนุนวิธีการดังกล่าว
  • กําหนดวิธีการใหม่ในคลาสที่ได้รับมา ไม่สามารถเพิ่มเมธอด ด้วยวิธีนี้โดยใช้การสืบทอดสําหรับชนิดอื่น ๆ เช่น โครงสร้างและการแจกแจง หรือสามารถใช้เพื่อ "เพิ่ม" วิธีการ ไปยังคลาสที่ปิดสนิท

วิธีการขยายช่วยให้คุณ "เพิ่ม" วิธีการไปยังประเภทที่มีอยู่โดยไม่ต้องแก้ไขประเภทเองหรือใช้วิธีการใหม่ในประเภทที่สืบทอดมา นอกจากนี้ เมธอดส่วนขยายไม่จําเป็นต้องอยู่ในแอสเซมบลีเดียวกันกับชนิดที่ขยายอีกด้วย คุณเรียกเมธอดส่วนขยายราวกับเป็นสมาชิกที่กําหนดไว้ของชนิด

วิธีการขยาย

วิธีการส่วนขยายช่วยให้คุณสามารถ "เพิ่ม" วิธีการไปยังประเภทที่มีอยู่โดยไม่ต้องสร้างประเภทที่ได้รับใหม่คอมไพล์หรือแก้ไขประเภทเดิม วิธีการขยายเป็นวิธีการแบบคงที่ แต่จะเรียกว่าราวกับว่าเป็นวิธีการอินสแตนซ์บนชนิดที่ขยาย สําหรับรหัสลูกค้าที่เขียนใน C#, F# และ Visual Basic ไม่มีความแตกต่างที่ชัดเจนระหว่างการเรียกใช้เมธอดส่วนขยายและวิธีการที่กําหนดไว้ในชนิด

วิธีการผูกส่วนขยายในเวลาการคอมไพล์

คุณสามารถใช้วิธีการขยายเพื่อขยายคลาสหรืออินเทอร์เฟซได้ แต่ไม่สามารถแทนที่ได้ เมธอดส่วนขยายที่มีชื่อและลายเซ็นเดียวกันกับวิธีการคลาสจะไม่ถูกเรียก ในเวลาการคอมไพล์ วิธีการขยายจะมีลําดับความสําคัญต่ํากว่าวิธีการอินสแตนซ์ที่กําหนดไว้ในชนิดเองเสมอ กล่าวอีกนัยหนึ่ง ถ้าชนิดมีเมธอด ที่ชื่อ Process(int i)และคุณมีเมธอดส่วนขยายที่มีลายเซ็นเดียวกัน คอมไพเลอร์จะผูกกับวิธีการอินสแตนซ์เสมอ เมื่อคอมไพเลอร์พบการเรียกใช้เมธอด จะค้นหาการจับคู่ในวิธีการอินสแตนซ์ของชนิด หากไม่พบวิธีการอินสแตนซ์ที่ตรงกัน คอมไพเลอร์จะค้นหาเมธอดส่วนขยายใด ๆ ที่กําหนดไว้สําหรับชนิด คอมไพเลอร์ผูกกับเมธอดส่วนขยายแรกที่พบ

ตัวอย่างต่อไปนี้แสดงให้เห็นถึงกฎที่คอมไพเลอร์ C# ทําตามในการกําหนดว่าจะผูกการเรียกเมธอด กับเมธอดอินสแตนซ์ในชนิด หรือวิธีการขยาย


namespace ExtensionMethodDemo.PersonNamespace
{
    // Define a simple class with properties and methods
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }

        public void Introduce()
        {
            Console.WriteLine($"Hi, I'm {FirstName} {LastName}, and I'm {Age} years old.");
        }
    }
}

namespace ExtensionMethodDemo.PersonExtensionsNamespace
{
    using ExtensionMethodDemo.PersonNamespace;

    // Define an extension method for the Person class
    public static class PersonExtensions
    {
        public static void DisplayFullName(this Person person)
        {
            Console.WriteLine($"Full Name: {person.FirstName} {person.LastName}");
        }

        public static bool IsAdult(this Person person)
        {
            return person.Age >= 18;
        }

        public static void Introduce()
        {
            Console.WriteLine($"Extension - Hi, it's nice to meet you.");
        }

        // Extension method attempting to override Introduce
        public static void Introduce(this Person person, string greeting)
        {
            Console.WriteLine($"{greeting}, I'm {person.FirstName} {person.LastName}, and I'm {person.Age} years old.");
        }
    }
}

namespace ExtensionMethodDemo
{
    using ExtensionMethodDemo.PersonNamespace;
    using ExtensionMethodDemo.PersonExtensionsNamespace;

    class Program
    {
        static void Main(string[] args)
        {
            // Create instances of the Person class
            Person person1 = new Person { FirstName = "FName1", LastName = "LName1", Age = 25 };
            Person person2 = new Person { FirstName = "FName2", LastName = "LName2", Age = 16 };

            // Use the methods of the Person class
            person1.Introduce();
            person2.Introduce();

            // Use the extension methods
            person1.DisplayFullName();
            Console.WriteLine($"Is {person1.FirstName} an adult? {person1.IsAdult()}");

            person2.DisplayFullName();
            Console.WriteLine($"Is {person2.FirstName} an adult? {person2.IsAdult()}");

            // Use the extension method that attempts to override Introduce
            person1.Introduce("Hello");
            person2.Introduce("Greetings");
        }
    }
}
// Output:
//     Hi, I'm FName1 LName1, and I'm 25 years old.
//     Hi, I'm FName2 LName2, and I'm 16 years old.
//     Full Name: FName1 LName1
//     Is FName1 an adult? True
//     Full Name: FName2 LName2
//     Is FName2 an adult? False
//     Hello, I'm FName1 LName1, and I'm 25 years old.
//     Greetings, I'm FName2 LName2, and I'm 16 years old.

ในตัวอย่างนี้ คลาส Person มีคุณสมบัติสามประการ: FirstName, LastName, และ Age คลาส Person ยังมีเมธอดอินสแตนซ์ Introduce ที่เขียนข้อความไปยังคอนโซล

คลาส PersonExtensions จะกําหนดวิธีการขยายสําหรับคลาส Person คลาส PersonExtensions มีวิธีการขยายต่อไปนี้:

  • DisplayFullName: เขียนชื่อเต็มของบุคคลไปยังคอนโซล
  • IsAdult: ส่งกลับค่าบูลีนที่ระบุว่าบุคคลนั้นเป็นผู้ใหญ่หรือไม่
  • Introduce: เขียนข้อความไปยังคอนโซล วิธีนี้มีโอเวอร์โหลดสองตัว: แบบหนึ่งไม่มีพารามิเตอร์และอีกตัวที่มีพารามิเตอร์สตริง
  • Introduce(this Person person, string greeting): เขียนข้อความไปยังคอนโซลด้วยคําทักทายแบบกําหนดเอง
  • Introduce(): เขียนข้อความเริ่มต้นไปยังคอนโซล

วิธี Main จะสร้างอินสแตนซ์สองอินสแตนซ์ของคลาส Person และเรียกใช้เมธอด Introduce ในแต่ละอินสแตนซ์ จะมีการดําเนินการวิธีการ Introduce ที่กําหนดไว้ในคลาส Person วิธี Introduce ที่ไม่มีพารามิเตอร์ในคลาส PersonExtensions ไม่ได้ถูกเรียกเนื่องจากคลาส Person มีเมธอดอินสแตนซ์ Introduce ด้วยลายเซ็นเดียวกันอยู่แล้ว คอมไพเลอร์เป็นไปตามกฎสําหรับการผูกวิธีการขยายในเวลาการคอมไพล์และให้ความสําคัญกับเมธอดอินสแตนซ์ที่กําหนดไว้ในชนิดเอง เมธอด Main ยังเรียกเมธอดส่วนขยาย DisplayFullName, IsAdultและวิธีการ Introduce โอเวอร์โหลดด้วยคําทักทายแบบกําหนดเอง วิธีการขยายจะดําเนินการตามที่คาดไว้

คําแนะนําทั่วไป

วิธีการขยายเป็นตัวเลือกที่สําคัญสําหรับการสร้างฟังก์ชันการทํางานที่นํามาใช้ใหม่ได้ทั่วทั้งระบบนิเวศของ .NET อย่างไรก็ตาม การแก้ไขโค้ดของวัตถุหรือใช้ชนิดใหม่เมื่อใดก็ตามที่เหมาะสมและเป็นไปได้ที่จะทําเช่นนั้นจะยังคงถือว่าเหมาะสม วิธีการขยายเป็นตัวเลือกที่ยอดเยี่ยมเมื่อแหล่งข้อมูลต้นฉบับไม่ได้อยู่ภายใต้การควบคุมของคุณเมื่อวัตถุที่ได้รับมาไม่เหมาะสมหรือไม่สามารถใช้ได้หรือเมื่อไม่ควรเปิดเผยฟังก์ชันการทํางานเกินขอบเขตที่เกี่ยวข้อง

เมื่อใช้วิธีการขยายเพื่อขยายชนิดที่คุณไม่ได้ควบคุมซอร์สโค้ด คุณเสี่ยงต่อการเปลี่ยนแปลงในการใช้งานประเภทจะทําให้เมธอดส่วนขยายของคุณเสียหาย หากคุณใช้วิธีการขยายสําหรับประเภทที่กําหนด โปรดจําไว้ว่าจุดต่อไปนี้:

  • ไม่มีการเรียกใช้เมธอดส่วนขยายถ้ามีลายเซ็นเดียวกันกับเมธอดที่กําหนดไว้ในชนิด
  • วิธีการขยายจะถูกนําเข้าสู่ขอบเขตที่ระดับ namespace ตัวอย่างเช่น ถ้าคุณมีคลาสแบบคงที่หลายชั้นที่มีวิธีการขยายใน namespace เดียวที่ชื่อ Extensionsสิ่งเหล่านั้นทั้งหมดจะถูกนําเข้าสู่ขอบเขตโดยคําสั่ง using Extensions;

สําหรับไลบรารีคลาสที่คุณใช้ คุณไม่ควรใช้เมธอดส่วนขยายเพื่อหลีกเลี่ยงการเพิ่มหมายเลขเวอร์ชันของแอสเซมบลี ถ้าคุณต้องการเพิ่มฟังก์ชันการทํางานที่สําคัญไปยังไลบรารีที่คุณเป็นเจ้าของโค้ดต้นฉบับ ให้ทําตามคําแนะนําของ .NET สําหรับการกําหนดรุ่นแอสเซมบลี