تعدد الأشكال: وقت التجميع مقابل وقت التشغيل
جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 25 من 70.
تعدد الأشكال (Polymorphism) يعني "أشكالاً متعددة" - وهي القدرة على أن يتصرف نفس الكود بشكل مختلف بناءً على السياق. تدعم لغة C# نوعين متميزين من تعدد الأشكال، وفهم متى يتم حل كل منهما يساعدك على كتابة كود أكثر مرونة.
تعدد الأشكال في وقت التجميع (يسمى أيضًا تعدد الأشكال الساكن) يتم حله بواسطة المترجم قبل تشغيل برنامجك. يعد التحميل الزائد للدوال (Method overloading) هو المثال الأساسي - حيث تشترك عدة دوال في نفس الاسم ولكن لها معاملات مختلفة:
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // المترجم يختار إصدار int
calc.Add(5.0, 3.0); // المترجم يختار إصدار doubleيقوم المترجم بفحص أنواع الوسائط ويختار الطريقة الصحيحة في وقت الترجمة. يتم تثبيت هذا القرار قبل أن يبدأ البرنامج في التنفيذ.
يتم حل تعدد الأشكال في وقت التشغيل (تعدد الأشكال الديناميكي) أثناء تشغيل البرنامج. يحدث هذا مع دوال virtual و override - حيث يحدد النوع الفعلي للكائن أي دالة سيتم تنفيذها:
Animal pet = new Dog(); // تم تعريفه كـ Animal، وهو في الواقع Dog
pet.Speak(); // يستدعي Speak() الخاصة بـ Dog في وقت التشغيللا يعرف المترجم أي Speak() ستعمل - هذا القرار يحدث في وقت التشغيل بناءً على النوع الحقيقي للكائن. هذا ما يجعل تعدد الأشكال القائم على الوراثة قوياً جداً لبناء أنظمة مرنة وقابلة للتوسع.
التحدي
سهللنقم ببناء نظام محول (converter) يوضح كلا نوعي تعدد الأشكال (polymorphism) بشكل عملي. ستقوم بإنشاء فئة تستخدم التحميل الزائد للدوال (method overloading) - وهو تعدد الأشكال في وقت التجميع (compile-time polymorphism) - إلى جانب هيكل وراثة يستخدم دوال virtual/override - وهو تعدد الأشكال في وقت التشغيل (runtime polymorphism).
ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:
Converter.cs: قم بتعريف فئةConverterداخل مساحة الأسماءConversion. توضح هذه الفئة تعدد الأشكال في وقت التجميع من خلال التحميل الزائد للدوال. قم بإنشاء ثلاث دوالConvertمحملة بشكل زائد:Convert(int value)تعيد"Integer: {value}"Convert(double value)تعيد"Double: {value}"Convert(string value)تعيد"String: {value}"
Shape.cs: قم بتعريف فئة أساسيةShapeداخل مساحة الأسماءConversion. تحتوي هذه الفئة على خاصيةName(سلسلة نصية) ومنشئ (constructor) يقوم بتعيينها. قم بتضمين دالةvirtualتسمىDescribe()تعيد"This is a {Name}".Circle.cs: قم بتعريف فئةCircleداخل مساحة الأسماءConversionترث منShape. أضف خاصيةRadius(double). يقبل المنشئ نصف القطر ويمرر"Circle"إلى منشئ الفئة الأساسية. قم بعمل override للدالةDescribe()لتعيد"This is a Circle with radius {Radius}".Program.cs: في ملفك الرئيسي، قم بتوضيح كلا نوعي تعدد الأشكال. أولاً، قم بإنشاء كائنConverterواستدعِ جميع الدوال الثلاث المحملة بشكل زائد مع قيم الإدخال. ثم قم بإنشاء كائنCircle، وقم بتخزينه في متغير من نوعShape، واستدعِDescribe()لإظهار تعدد الأشكال في وقت التشغيل.
ستتلقى أربعة مدخلات:
- قيمة صحيحة (integer)
- قيمة عشرية (double)
- قيمة نصية (string)
- نصف قطر الدائرة (double)
اطبع المخرجات بهذا التنسيق:
Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}على سبيل المثال، إذا كانت المدخلات هي 42، و 3.14، و Hello، و 5.5، يجب أن تكون المخرجات:
Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5لاحظ الفرق الجوهري: يقرر المترجم (compiler) أي دالة Convert سيتم استدعاؤها بناءً على أنواع الوسائط في وقت التجميع، بينما يتم تحديد دالة Describe() في وقت التشغيل بناءً على نوع الكائن الفعلي، حتى لو كان مخزناً في متغير من نوع Shape!
ورقة مرجعية
تعدد الأشكال (Polymorphism) يعني "أشكالاً متعددة" - وهي قدرة الكود نفسه على التصرف بشكل مختلف بناءً على السياق.
تعدد الأشكال في وقت التجميع (تعدد الأشكال الساكن) يتم حله بواسطة المترجم قبل تشغيل البرنامج. يعد التحميل الزائد للدوال (Method overloading) هو المثال الأساسي:
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // المترجم يختار نسخة int
calc.Add(5.0, 3.0); // المترجم يختار نسخة doubleيقوم المترجم بفحص أنواع الوسائط ويختار الطريقة الصحيحة في وقت التجميع.
تعدد الأشكال في وقت التشغيل (تعدد الأشكال الديناميكي) يتم حله أثناء تشغيل البرنامج باستخدام طرق virtual و override:
Animal pet = new Dog(); // تم التصريح عنه كـ Animal، ولكنه في الواقع Dog
pet.Speak(); // يستدعي Speak() الخاصة بـ Dog في وقت التشغيلالنوع الفعلي للكائن هو الذي يحدد الطريقة التي سيتم تنفيذها في وقت التشغيل، وليس نوع المتغير المصرح عنه.
جرّب بنفسك
using System;
using Conversion;
class Program
{
public static void Main(string[] args)
{
// قراءة المدخلات
int intValue = Convert.ToInt32(Console.ReadLine());
double doubleValue = Convert.ToDouble(Console.ReadLine());
string stringValue = Console.ReadLine();
double radius = Convert.ToDouble(Console.ReadLine());
// TODO: توضيح Compile-time Polymorphism
// إنشاء كائن Converter واستدعاء جميع طرق Convert الثلاثة المحملة بشكل زائد (overloaded)
Console.WriteLine("Compile-time Polymorphism:");
// استدعاء Convert مع intValue و doubleValue و stringValue
// TODO: توضيح Runtime Polymorphism
// إنشاء كائن Circle وتخزينه في متغير Shape
// ثم استدعاء Describe() على متغير Shape
Console.WriteLine("Runtime Polymorphism:");
// إنشاء Circle واستدعاء Describe()
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةNamespaces والتوجيهاتمقدمة في Classes و Objectsالكلمة المفتاحية 'this'Methods والمعاملاتFields مقابل PropertiesConstructorsObject Initializersمراجعة - آلة حاسبة بسيطة4الوراثة
صيغة الوراثة الأساسية (:)الكلمة المفتاحية 'base'الكلمات المفتاحية Virtual و Overrideالأصناف المختومة (Sealed Classes)الفئة الأساسية 'object'مراجعة - هيكلية الموظفين7ميزات متقدمة
تحميل العوامل (Operator Overloading)المفهرسات (Indexers (this[]))إعادة تعريف ()ToStringدوال التوسعة (Extension Methods)ملخص - Custom List2الخصائص والأعضاء الساكنة (Static Members)
الخصائص المطبقة تلقائيًا (Auto-Implemented Properties)خصائص القراءة فقط والكتابة فقطالحقول والدوال الساكنة (Static)الفئات الساكنة (Static Classes)الأعضاء ذات البنية التعبيرية (Expression-Bodied Members)5تعدد الأشكال والواجهات (Polymorphism & Interfaces)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالواجهة مقابل الفئة المجردة (Abstract Class)الواجهات المتعددةالواجهات الصريحةUpcasting و Downcastingملخص - حاسبة الأشكال8مفاهيم البرمجة كائنية التوجه (OOP) المتقدمة
التركيب بدلاً من الوراثةالـ Generics (الأصناف والدوال)الـ Delegates والأحداثالـ Attributes والـ Reflectionواجهة IDisposable وجملة usingأساسيات Dependency Injection