Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

تعدد الأشكال: وقت التجميع مقابل وقت التشغيل

جزء من قسم 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() ستعمل - هذا القرار يحدث في وقت التشغيل بناءً على النوع الحقيقي للكائن. هذا ما يجعل تعدد الأشكال القائم على الوراثة قوياً جداً لبناء أنظمة مرنة وقابلة للتوسع.

challenge icon

التحدي

سهل

لنقم ببناء نظام محول (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()
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming