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