لماذا هذا مهم: مُنشئ واحد جيد، لكن اثنين أفضل
\n\nهل سبق لك أن كتبت كلاسًا في C# ولاحظت أن لديك عدة مُنشئات تقوم بنفس الشيء تقريبًا؟ على سبيل المثال، واحد يقبل الاسم والعمر، وآخر يقبل الاسم فقط، وثالث لا يقبل أي شيء. بدلاً من نسخ نفس الكود في كل مُنشئ، تقدم C# حلاً أنيقًا: استدعاء مُنشئ من مُنشئ آخر. هذا لا يوفر الوقت فقط، بل يجعل الكود أنظف وأسهل في الفهم والصيانة. اليوم سنشرح هذا الموضوع بأمثلة واقعية، وكأنك صادفت للتو سؤالاً شائعًا على Stack Overflow. هيا بنا!
\n\nكيف يعمل استدعاء المُنشئ من مُنشئ آخر في C#
\n\nفي C#، يمكنك استدعاء مُنشئ من مُنشئ آخر باستخدام الكلمتين المفتاحيتين this و base. يُسمى هذا "سلسلة المُنشئات" (constructor chaining). دعنا نشرح كلا الخيارين.
1. الكلمة المفتاحية this — استدعاء مُنشئ في نفس الكلاس
\n\nعندما يكون لديك عدة مُنشئات في نفس الكلاس، يمكنك استخدام this لتمرير التحكم إلى أحدها. هذا مفيد بشكل خاص عندما يكون أحد المُنشئات "تعميمًا" للآخر.
مثال بسيط: كلاس Person يخزن الاسم والعمر. المُنشئ الافتراضي يحدد قيمًا افتراضية، بينما المُنشئ المُعامل يسمح بتجاوزها.
public class Person\n{\n public string Name { get; set; }\n public int Age { get; set; }\n\n // المُنشئ الافتراضي\n public Person() : this("ضيف", 18)\n {\n // لا نكتب شيئًا هنا — السلسلة تقوم بكل شيء\n }\n\n // المُنشئ مع المعاملات\n public Person(string name, int age)\n {\n Name = name;\n Age = age;\n }\n}\n\nفي هذا الكود، النقطة الأساسية هي : this("ضيف", 18). عندما تنشئ كائنًا باستخدام new Person()، يتم استدعاء المُنشئ بدون معاملات، لكنه يمرر التحكم فورًا إلى المُنشئ ذي المعاملين، ويمرر له القيمتين "ضيف" و 18. بهذه الطريقة، يكون منطق التهيئة بالكامل في مكان واحد.
2. الكلمة المفتاحية base — استدعاء مُنشئ الكلاس الأساسي
\n\nإذا كان كلاسك يرث من كلاس آخر، يمكنك استدعاء مُنشئ الكلاس الأساسي عبر base. هذا يضمن تهيئة الجزء الأساسي من الكائن بشكل صحيح.
مثال: الكلاس الأساسي Animal والكلاس المشتق Dog.
public class Animal\n{\n public string Species { get; set; }\n\n public Animal(string species)\n {\n Species = species;\n }\n}\n\npublic class Dog : Animal\n{\n public string Breed { get; set; }\n\n // مُنشئ Dog يستدعي مُنشئ Animal\n public Dog(string breed) : base("كلب")\n {\n Breed = breed;\n }\n}\n\nهنا، : base("كلب") يخبر المترجم: "قم أولاً بتنفيذ مُنشئ الكلاس الأساسي Animal مع الوسيطة 'كلب'، ثم تابع تهيئة Dog". بدون هذا، كنت ستحصل على خطأ في الترجمة، لأن Animal ليس لديه مُنشئ افتراضي.
مثال واقعي: سؤال شائع من Stack Overflow
\n\nتخيل أنك تعمل على تطبيق لمتجر إلكتروني. لديك كلاس Order يمكن إنشاؤه بطرق مختلفة: مع معرف العميل، مع مجموعة كاملة من البيانات، أو بدون معاملات (للاختبارات).
public class Order\n{\n public int OrderId { get; set; }\n public int CustomerId { get; set; }\n public decimal TotalAmount { get; set; }\n public DateTime CreatedDate { get; set; }\n\n // المُنشئ بدون معاملات — للاختبارات\n public Order() : this(0, 0, 0)\n {\n }\n\n // المُنشئ مع الحد الأدنى من البيانات\n public Order(int customerId, decimal totalAmount) : this(0, customerId, totalAmount)\n