Generics في C#: دليل شامل للاستخدام والفوائد

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

Generics في C#: لماذا نحتاجها وكيف نستخدمها



الأنواع العامة (Generics) هي إحدى الإمكانيات الرئيسية للغة C#، والتي تتيح إنشاء كلاسات وطرق وواجهات بأنواع معلمة. بدلاً من كتابة نفس الكود لكل نوع بيانات، تكتبه مرة واحدة، وتحدد النوع عند الاستخدام. هذا يحسن الأداء، وسلامة الأنواع، وإعادة استخدام الكود. في هذه المقالة، سنشرح لماذا نحتاج Generics، وكيف نطبقها عملياً، ونستعرض أمثلة حقيقية من .NET.



لماذا نحتاج Generics في C#



قبل ظهور Generics في C# (الإصدار 2.0)، كان المطورون يستخدمون غالباً النوع object والمجموعات ArrayList. أدى هذا إلى مشكلتين:



  • فقدان الأداء — التغليف (boxing) وإلغاء التغليف (unboxing) للأنواع ذات القيمة (int, double, struct).
  • غياب التحقق من الأنواع في وقت الترجمة — يمكن وضع سلسلة نصية عن طريق الخطأ في قائمة أرقام والحصول على خطأ فقط في وقت التشغيل.


تحل Generics كلتا المشكلتين. فهي تسمح بإنشاء هياكل بيانات آمنة من حيث النوع دون تكاليف إضافية غير ضرورية. على سبيل المثال، List<int> يعمل مع int بدون تغليف، ولن يسمح المترجم بإضافة سلسلة نصية إليه.



المفاهيم الأساسية لـ Generics



الكلاسات العامة



يتم تعريف الكلاس العام (Generic class) بمعامل نوع بين قوسين زاويتين. معامل النوع هو placeholder يتم استبداله بنوع حقيقي عند إنشاء نسخة.



public class Box<T>{    private T _value;

public Box(T value) { _value = value; }

public T GetValue() => _value;}


الاستخدام:



var intBox = new Box<int>(42);var stringBox = new Box<string>("Hello");

Console.WriteLine(intBox.GetValue()); // 42Console.WriteLine(stringBox.GetValue());// Hello


الطرق العامة



يمكن أن يكون للطريقة معامل النوع الخاص بها، حتى لو لم يكن الكلاس عاماً:



public class Utils{    public static void Swap<T>(ref T a, ref T b)    {        T temp = a;        a = b;        b = temp;    }}

// الاستخدامint x = 1, y = 2;Utils.Swap(ref x, ref y);Console.WriteLine($"{x} {y}"); // 2 1


الواجهات العامة



الواجهات ذات معامل النوع تسمح بإنشاء تجريدات مرنة:



public interface IRepository<T>{    T GetById(int id);    void Add(T entity);}

public class UserRepository : IRepository<User>{ public User GetById(int id) => new User(); public void Add(User user) { }}


القيود (Constraints) في Generics



في بعض الأحيان، نحتاج إلى فرض قيود على الأنواع التي يمكن استخدامها. لهذا، نستخدم الكلمة المفتاحية where.



الأنواع الرئيسية للقيود



  • where T : class — فقط الأنواع المرجعية (string, كلاسات).
  • where T : struct — فقط الأنواع ذات القيمة (int, DateTime, struct).
  • where T : new() — يجب أن يحتوي النوع على مُنشئ عام بدون معاملات.
  • where T : SomeBaseClass — يجب أن يكون النوع أو يرث من الكلاس المحدد.
  • where T : ISomeInterface — يجب أن يقوم النوع بتطبيق الواجهة.


مثال مع قيود متعددة:



public class Repository<T> where T : class, IEntity, new(){    public T Create()    {        return new T(); // مسموح به بفضل new()    }}


Covariance و Contravariance في Generics



ابتداءً من C# 4.0، أصبح من الممكن التحكم في تباين الواجهات العامة باستخدام الكلمات المفتاحية in و out.



  • التباين المشترك (Covariance) (out) — يسمح باستخ

المدونات

توصيات الكتب