C#: الأعضاء الثابتة وطرق التمديد

1. الحقول والطرق الثابتة

تنتمي عناصر static إلى النوع نفسه وليس إلى أي مثيل. وتشترك جميع المثيلات في الحقل الثابت نفسه. ويتم استدعاء الطرق الثابتة عبر ClassName.Member دون إنشاء كائن.

▶ مثال

CSHARP
class Counter
{
    public static int TotalCount = 0;
    public int InstanceId;

    public Counter(int id)
    {
        InstanceId = id;
        TotalCount++;
    }

    public static void Report()
    {
        Console.WriteLine($"{TotalCount} instances created");
    }
}

Counter a = new Counter(1);
Counter b = new Counter(2);
Counter.Report();
Console.WriteLine(Counter.TotalCount);
▶ جرّب الكود
TEXT 📖 للعرض فقط
2 instances created
2
💡 لا يمكن للطرق static استخدام this ولا يمكنها الوصول مباشرةً إلى عناصر المثيل — بل يمكنها فقط الوصول إلى العناصر الثابتة الأخرى.


2. منشئات ثابتة

يتم تنفيذ المنشئ الثابت تلقائيًا مرة واحدة فقط عند الوصول إلى الفئة لأول مرة، ولا يتكرر ذلك. ولا يمكن أن يحتوي على مُعدِّلات الوصول أو معلمات، ويُستخدم عادةً لتهيئة الحقول الثابتة.

▶ مثال

CSHARP
class Config
{
    public static readonly DateTime StartTime;

    static Config()
    {
        StartTime = DateTime.Now;
        Console.WriteLine("Static constructor executed");
    }
}

Console.WriteLine("Preparing to access Config");
Console.WriteLine(Config.StartTime);
Console.WriteLine(Config.StartTime);
▶ جرّب الكود
TEXT 📖 للعرض فقط
Preparing to access Config
Static constructor executed
2026/06/28 10:00:00
2026/06/28 10:00:00
⚠️ لا يتم تشغيل المنشئ الثابت إلا مرة واحدة — ولن يؤدي الوصول إلى العناصر الثابتة للفئة عدة مرات إلى تنفيذه مرة أخرى.


3. الفئات الثابتة (نمط فئة الأدوات المساعدة)

لا يمكن إنشاء مثيل للفئة المُعلنة باستخدام static class أو توريثها. يجب أن تكون جميع العناصر التابعة لها ثابتة، مما يجعلها مناسبة لتغليف طرق المساعدة.

▶ مثال

CSHARP
static class MathHelper
{
    public static double Square(double x) => x * x;
    public static double Hypotenuse(double a, double b) => Math.Sqrt(Square(a) + Square(b));
}

Console.WriteLine(MathHelper.Square(3));
Console.WriteLine(MathHelper.Hypotenuse(3, 4));
▶ جرّب الكود
TEXT 📖 للعرض فقط
9
5
📌 تؤدي محاولة new MathHelper() إلى حدوث خطأ في التحويل البرمجي — لا يمكن إنشاء مثيلات للفئات الثابتة.


4. const مقابل static readonly

مقال خاص const static readonly
عند التعيين وقت التحويل البرمجي وقت التشغيل
أنواع القيم الأنواع المدمجة فقط أي نوع
قابلية التغيير غير قابل للتغيير غير قابل للتغيير بعد التعيين في مُنشئ

▶ مثال

CSHARP
class Settings
{
    public const int MaxRetry = 3;
    public static readonly DateTime LaunchTime;

    static Settings()
    {
        LaunchTime = DateTime.Now;
    }
}

Console.WriteLine(Settings.MaxRetry);
Console.WriteLine(Settings.LaunchTime);
▶ جرّب الكود
TEXT 📖 للعرض فقط
3
2026/06/28 10:00:00
⚠️ يتم تضمين قيم const في تجميع البرنامج المستدعي في وقت التحويل البرمجي. وفي حال تعديل قيمة ثابتة (const)، يجب إعادة تحويل جميع التجميعات التي تشير إليها. أما static readonly فيتم قراءتها في وقت التشغيل، مما يجعلها أكثر أمانًا.


5. طرق التمديد

تقوم طرق التوسيع بـ«إضافة» طرق إلى نوع موجود دون تعديله. يتم تعريفها في فئة ثابتة، وتُسبق المعلمة الأولى بكلمة this، مما يسمح باستدعائها كما لو كانت طرق مثيل.

▶ مثال

CSHARP
static class StringExt
{
    public static bool IsEmpty(this string s) => s.Length == 0;
    public static string Repeat(this string s, int count)
    {
        var sb = new StringBuilder();
        for (int i = 0; i < count; i++) sb.Append(s);
        return sb.ToString();
    }
}

Console.WriteLine("hello".IsEmpty());
Console.WriteLine("ab".Repeat(3));
▶ جرّب الكود
TEXT 📖 للعرض فقط
False
ababab
💡 طرق التمديد هي في الأساس استدعاءات لطرق ثابتة — حيث يقوم المُترجم بترجمة s.IsEmpty() إلى StringExt.IsEmpty(s). وتكون لأساليب المثيل الأسبقية على طرق التمديد.


6. طرق التوسيع في الممارسة العملية (أساسيات LINQ)

تستند صيغة LINQ السلسة إلى طرق التمديد. Where وSelect وOrderBy وغيرها في System.Linq.Enumerable هي جميعها طرق تمديد لـ IEnumerable<T>.

▶ مثال

CSHARP
static class EnumerableExt
{
    public static IEnumerable```<T>``` MyWhere```<T>```(this IEnumerable```<T>``` source, Func`<T, bool>` predicate)
    {
        foreach (var item in source)
        {
            if (predicate(item)) yield return item;
        }
    }
}

int[] nums = { 1, 2, 3, 4, 5, 6 };
var even = nums.MyWhere(n => n % 2 == 0);
Console.WriteLine(string.Join(", ", even));
▶ جرّب الكود
TEXT 📖 للعرض فقط
2, 4, 6
🚀 بمجرد أن تفهم كيفية عمل أساليب التمديد، تصبح قراءة كود مصدر LINQ أمرًا سهلاً — فهي ببساطة مجموعة من أساليب التمديد المصممة بعناية.


7. الفئات الجزئية

تسمح الكلمة الرئيسية partial بتقسيم تعريف الفئة على عدة ملفات، والتي يتم دمجها في فئة واحدة كاملة في وقت التحويل البرمجي. ويُستخدم هذا عادةً لفصل الكود الذي يتم إنشاؤه تلقائيًّا عن الكود المكتوب يدويًّا.

▶ مثال

CSHARP
partial class User
{
    public string Name { get; set; }
    public User(string name) => Name = name;
}

partial class User
{
    public void Greet() => Console.WriteLine($"Hello, {Name}");
}

var u = new User("Alice");
u.Greet();
▶ جرّب الكود
TEXT 📖 للعرض فقط
Hello, Alice
📌 تستخدم أطر العمل مثل WinForms وWPF وEF Core الفئات الجزئية على نطاق واسع لفصل الكود الذي يُنشئه المصمم عن المنطق التجاري.


8. الطرق الجزئية

يتم إعلان الدالة الجزئية في جزء من الفئة الجزئية، ويجري تنفيذها اختياريًّا في جزء آخر. وفي حالة عدم تنفيذها، يقوم المُجمِّع بإزالة موقع الاستدعاء — دون أي عبء إضافي. وابتداءً من الإصدار C# 9، لم تعد الدوال الجزئية تخضع لقيود التوقيع.

▶ مثال

CSHARP
partial class Order
{
    public int Amount { get; set; }
    partial void OnCreated();

    public Order(int amount)
    {
        Amount = amount;
        OnCreated();
    }
}

partial class Order
{
    partial void OnCreated() => Console.WriteLine("Order created");
}

var o = new Order(100);
Console.WriteLine(o.Amount);
▶ جرّب الكود
TEXT 📖 للعرض فقط
Order created
100
💡 إذا قمت بإزالة تنفيذ OnCreated من الفئة الجزئية الثانية، فسيظل البرنامج قابلاً للتحويل البرمجي، وسيقوم المُحوِّل البرمجي بإزالة موقع الاستدعاء تلقائيًّا.


❓ أسئلة شائعة

س هل يمكن للطرق الثابتة الوصول إلى حقول المثيل؟
ج لا. فالطرق الثابتة غير مرتبطة بأي مثيل — يجب عليك استخدام مرجع كائن للوصول إلى عناصر المثيل.
س هل يجب عليّ استخدام const أم static readonly؟
ج استخدم const للثوابت البسيطة المعروفة في وقت التحويل البرمجي؛ واستخدم static readonly للقيم التي يتم تحديدها في وقت التشغيل أو لأنواع المرجع.
س هل يمكن لطرق التمديد الوصول إلى العناصر الخاصة؟
ج لا. طرق التمديد هي في الأساس طرق ثابتة ولا يمكنها الوصول إلا إلى العناصر العامة.
س هل يجب أن تكون جميع أجزاء الفئة الجزئية ضمن نفس التجميع؟
ج نعم، يجب أن تكون جميع أجزاء الفئة الجزئية ضمن نفس التجميع ونفس مساحة الاسم.

📖 ملخص

📝 تمارين

  1. اكتب static class ArrayHelper مع الطرق الثابتة Max(int[] arr) وAverage(int[] arr)، واختبرها في Main
  2. اكتب طريقة تمديد MySum لـ IEnumerable<int> تحسب مجموع متتابعة أعداد صحيحة دون استخدام LINQ
  3. قم بإنشاء partial class Logger — في جزء منه، قم بتعريف partial void OnLog(string message) وطريقة Log(string msg) (التي تستدعي OnLog داخليًّا)؛ وفي الجزء الآخر، قم بتنفيذ OnLog للكتابة إلى وحدة التحكم
  4. قم بتعريف حقل const وحقل static readonly، واكتب كودًا للتحقق من أن const يتم استبداله في وقت التحويل البرمجي، بينما يتم قراءة static readonly في وقت التشغيل (تلميح: قم بالإشارة إليهما من تجميع آخر ولاحظ السلوك بعد التعديل)
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%