C#: الأعضاء الثابتة وطرق التمديد
1. الحقول والطرق الثابتة
تنتمي عناصر static إلى النوع نفسه وليس إلى أي مثيل. وتشترك جميع المثيلات في الحقل الثابت نفسه. ويتم استدعاء الطرق الثابتة عبر ClassName.Member دون إنشاء كائن.
▶ مثال
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);
2 instances created
2
static استخدام this ولا يمكنها الوصول مباشرةً إلى عناصر المثيل — بل يمكنها فقط الوصول إلى العناصر الثابتة الأخرى.
2. منشئات ثابتة
يتم تنفيذ المنشئ الثابت تلقائيًا مرة واحدة فقط عند الوصول إلى الفئة لأول مرة، ولا يتكرر ذلك. ولا يمكن أن يحتوي على مُعدِّلات الوصول أو معلمات، ويُستخدم عادةً لتهيئة الحقول الثابتة.
▶ مثال
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);
Preparing to access Config
Static constructor executed
2026/06/28 10:00:00
2026/06/28 10:00:00
3. الفئات الثابتة (نمط فئة الأدوات المساعدة)
لا يمكن إنشاء مثيل للفئة المُعلنة باستخدام static class أو توريثها. يجب أن تكون جميع العناصر التابعة لها ثابتة، مما يجعلها مناسبة لتغليف طرق المساعدة.
▶ مثال
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));
9
5
new MathHelper() إلى حدوث خطأ في التحويل البرمجي — لا يمكن إنشاء مثيلات للفئات الثابتة.
4. const مقابل static readonly
| مقال خاص | const |
static readonly |
|---|---|---|
| عند التعيين | وقت التحويل البرمجي | وقت التشغيل |
| أنواع القيم | الأنواع المدمجة فقط | أي نوع |
| قابلية التغيير | غير قابل للتغيير | غير قابل للتغيير بعد التعيين في مُنشئ |
▶ مثال
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);
3
2026/06/28 10:00:00
const في تجميع البرنامج المستدعي في وقت التحويل البرمجي. وفي حال تعديل قيمة ثابتة (const)، يجب إعادة تحويل جميع التجميعات التي تشير إليها. أما static readonly فيتم قراءتها في وقت التشغيل، مما يجعلها أكثر أمانًا.
5. طرق التمديد
تقوم طرق التوسيع بـ«إضافة» طرق إلى نوع موجود دون تعديله. يتم تعريفها في فئة ثابتة، وتُسبق المعلمة الأولى بكلمة this، مما يسمح باستدعائها كما لو كانت طرق مثيل.
▶ مثال
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));
False
ababab
s.IsEmpty() إلى StringExt.IsEmpty(s). وتكون لأساليب المثيل الأسبقية على طرق التمديد.
6. طرق التوسيع في الممارسة العملية (أساسيات LINQ)
تستند صيغة LINQ السلسة إلى طرق التمديد. Where وSelect وOrderBy وغيرها في System.Linq.Enumerable هي جميعها طرق تمديد لـ IEnumerable<T>.
▶ مثال
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));
2, 4, 6
7. الفئات الجزئية
تسمح الكلمة الرئيسية partial بتقسيم تعريف الفئة على عدة ملفات، والتي يتم دمجها في فئة واحدة كاملة في وقت التحويل البرمجي. ويُستخدم هذا عادةً لفصل الكود الذي يتم إنشاؤه تلقائيًّا عن الكود المكتوب يدويًّا.
▶ مثال
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();
Hello, Alice
8. الطرق الجزئية
يتم إعلان الدالة الجزئية في جزء من الفئة الجزئية، ويجري تنفيذها اختياريًّا في جزء آخر. وفي حالة عدم تنفيذها، يقوم المُجمِّع بإزالة موقع الاستدعاء — دون أي عبء إضافي. وابتداءً من الإصدار C# 9، لم تعد الدوال الجزئية تخضع لقيود التوقيع.
▶ مثال
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);
Order created
100
OnCreated من الفئة الجزئية الثانية، فسيظل البرنامج قابلاً للتحويل البرمجي، وسيقوم المُحوِّل البرمجي بإزالة موقع الاستدعاء تلقائيًّا.
❓ أسئلة شائعة
const أم static readonly؟const للثوابت البسيطة المعروفة في وقت التحويل البرمجي؛ واستخدم static readonly للقيم التي يتم تحديدها في وقت التشغيل أو لأنواع المرجع.📖 ملخص
- تُشارك الحقول
staticبين جميع المثيلات؛ ويتم استدعاء الطرقstaticعبر اسم الفئة - تُنفَّذ مُنشِئات العناصر الثابتة مرة واحدة عند الوصول إلى الفئة لأول مرة، وتُستخدم لتهيئة العناصر الثابتة
- لا يمكن إنشاء مثيل لـ
static classأو توريثه — وهو ما يجعله مثاليًّا لفئات الأدوات المساعدة constهو ثابت في وقت التحويل البرمجي؛static readonlyهو ثابت في وقت التشغيل- تستخدم طرق التوسيع المعلمة
thisلإضافة طرق إلى الأنواع الموجودة — وهو الأساس الذي يقوم عليه LINQ partialتُقسّم الفئات تعريفاتها إلى ملفات متعددة، ويتم دمجها في وقت التحويل البرمجي- توفر طرق
partial«هوكات» اختيارية — حيث يقوم المُجمِّع بإزالة المكالمات غير المنفَّذة
📝 تمارين
- اكتب
static class ArrayHelperمع الطرق الثابتةMax(int[] arr)وAverage(int[] arr)، واختبرها في Main - اكتب طريقة تمديد
MySumلـIEnumerable<int>تحسب مجموع متتابعة أعداد صحيحة دون استخدام LINQ - قم بإنشاء
partial class Logger— في جزء منه، قم بتعريفpartial void OnLog(string message)وطريقةLog(string msg)(التي تستدعي OnLog داخليًّا)؛ وفي الجزء الآخر، قم بتنفيذ OnLog للكتابة إلى وحدة التحكم - قم بتعريف حقل
constوحقلstatic readonly، واكتب كودًا للتحقق من أنconstيتم استبداله في وقت التحويل البرمجي، بينما يتم قراءةstatic readonlyفي وقت التشغيل (تلميح: قم بالإشارة إليهما من تجميع آخر ولاحظ السلوك بعد التعديل)