مقدمة إلى Generics في C#
Generics (الأنواع العامة) هي واحدة من أقوى الإمكانيات في لغة C#، والتي ظهرت في .NET Framework 2.0. تتيح لك إنشاء كلاسات وطرق وواجهات تعمل مع أي نوع بيانات مع الحفاظ على الكتابة القوية (strong typing). في هذه المقالة، سنشرح لماذا نحتاج إلى Generics في C#، وكيفية استخدامها، وما هي المزايا التي تقدمها للمطور.
تخيل أنك بحاجة إلى كتابة طريقة لتبادل قيم متغيرين. بدون Generics، سيتعين عليك إنشاء طرق منفصلة لكل نوع: SwapInt، SwapString، SwapDouble وهكذا. مع Generics، يتم حل هذه المهمة بدالة واحدة شاملة.
المزايا الرئيسية لـ Generics
1. أمان الأنواع في مرحلة الترجمة
توفر Generics التحقق من الأنواع أثناء الترجمة (compile time) وليس أثناء التنفيذ (runtime). هذا يعني أن الأخطاء المتعلقة بعدم تطابق الأنواع سيتم اكتشافها في مرحلة مبكرة من التطوير.
// بدون Generics - قد يحدث خطأ أثناء التنفيذArrayList list = new ArrayList();list.Add(123);list.Add("نص");int number = (int)list[1]; // InvalidCastException!
// مع Generics - خطأ في مرحلة الترجمةList<int> numbers = new List<int>();numbers.Add(123);numbers.Add("نص"); // خطأ في الترجمة!2. الأداء
استخدام Generics يتجنب عمليات التغليف (boxing) وفك التغليف (unboxing) للأنواع القيمية (value types). هذا يحسن الأداء بشكل كبير عند العمل مع المجموعات (collections).
// Boxing/Unboxing - بطيءArrayList list = new ArrayList();list.Add(42); // boxingint value = (int)list[0]; // unboxing
// Generics - سريع، بدون boxingList<int> list = new List<int>();list.Add(42); // بدون boxingint value = list[0]; // بدون unboxing3. إعادة استخدام الكود
يمكن لكلاس أو طريقة generic واحدة العمل مع أي نوع بيانات، مما يلغي الحاجة إلى تكرار الكود.
إنشاء واستخدام Generics
كلاسات Generic
لنلقِ نظرة على مثال لإنشاء كلاس عام لتخزين زوج من القيم:
public class Pair<TFirst, TSecond>{ public TFirst First { get; set; } public TSecond Second { get; set; } public Pair(TFirst first, TSecond second) { First = first; Second = second; } public void Display() { Console.WriteLine($"First: {First}, Second: {Second}"); }}
// الاستخدامvar pair1 = new Pair<int, string>(1, "واحد");var pair2 = new Pair<string, double>("PI", 3.14159);
pair1.Display(); // First: 1, Second: واحدpair2.Display(); // First: PI, Second: 3.14159طرق Generic
يمكن أن تكون الطرق أيضًا عامة. إليك مثال كلاسيكي — طريقة تبادل القيم:
public static void Swap<T>(ref T a, ref T b){ T temp = a; a = b; b = temp;}
// الاستخدامint x = 5, y = 10;Swap<int>(ref x, ref y);Console.WriteLine($"x = {x}, y = {y}"); // x = 10, y = 5
string s1 = "Hello", s2 = "World";Swap(ref s1, ref s2); // يمكن حذف النوعConsole.WriteLine($"s1 = {s1}, s2 = {s2}"); // s1 = World, s2 = Helloواجهات Generic
الواجهات مع Generics تتيح إنشاء عقود مرنة:
public interface IRepository<T>{ T GetById(int id); void Add(T entity); void Update(T entity); void Delete(int id);}
public class UserRepository : IRepository<User>{ public User GetById(int id) { // تنفيذ الحصول على المستخدم return new User(); } public void Add(User user) { // تنفيذ الإضافة } public void Update(User user) { // تنفيذ التحديث }