تعريف الـ Enum وكيفية استخدامه

السلام عليكم ورحمة الله وبركاته

وقت القراءة: ≈ 5 دقائق

المقدمة

في هذه المقالة سنتعرف على الـ Enum وما هي الفائدة منه وكيفية استخدامه في البرمجة
والـ Enum هو اختصار لـ Enumeration وهو أداة تقدمها لك لغات البرمجة لكي تستطيع تعريف مجموعة من القيم الثابتة تحت مسمى واحد

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

فمثلًا يمكننا أن نقول أننا سنرمز للمدير بالرقم 1 وللموظف بالرقم 2 وهكذا
هذا حل جيد ومقبول، هذا يعني أننا حين نرى الرقم 1 نعرف أنه يمثل المدير والرقم 2 يمثل الموظف

int EmployeeType = 1;

if (EmployeeType == 1) {
    cout << "This is a Manager";
} else if (EmployeeType == 2) {
    cout << "This is an Employee";
}

لكن هل تعتقد أن هذا الحل جيد؟
بصراحة لا، لأنك هنا تعتمد أرقام رمزية لتمثيل القيم وهذا يجعل الكود صعب الفهم والتعامل معه
فتخيل أنك لديك 10 أنواع مختلفة من الرتب وتريد ترميزها جميعًا، هل ستقوم بترميز كل رتبة برقم؟
المشكلة الحقيقية هي هل ستتذكر كل رقم يمثل أي رتبة؟
قد تتذكر، لكن سيأتي وقت ستنسى ماذا يمثل الرقم 5 مثلًا أوالرقم 7 وهكذا
لأن الأرقام لا تعطيك معلومات عن القيم التي تمثلها، هي مجرد أرقام لا تعني شيئًا

ستقول لي يمكننا استبدال الأرقام بالـ string هكذا سيكون الكود أكثر وضوحًا

string EmployeeType = "Manager";

if (EmployeeType == "Manager") {
    cout << "This is a Manager";
} else if (EmployeeType == "Employee") {
    cout << "This is an Employee";
}

حسنًا هذا حل جيد، لكن هناك مشكلة أخرى، هل تعتقد أنه من الجيد استخدام الـ string لتمثيل القيم؟
المشكلة هنا أنك قد تخطي الكتابة بالخطأ وتكتب "Manger" بدلاً من "Manager" وهكذا أو تكتبها هكذا "manager"
الأخطاء الإملائية هذه قد تحدث وتؤدي إلى مشاكل في البرنامج

هنا يأتي دور الـ Enum لحل هذه المشكلة، فالـ Enum يمكنك من تعريف مجموعة من القيم الثابتة تحت مسمى واحد
وهذه المسمى لا يتغير ولا يمكنك تغيير قيمها، وهذا يعني أنك لن تخطئ في كتابة القيمة بالخطأ

كيفية تعريف الـ Enum ؟

جميع اللغات تدعم الـ Enum ولكن الطريقة تختلف من لغة لأخرى، لكن الفكرة واحدة كما ذكرنا

في لغة الـ C++ يمكنك تعريف الـ Enum كالتالي

enum EmployeeType {
    Manager,
    Employee,
    Trainee,
    Volunteer
};

هنا قمنا بتعريف Enum بإسم EmployeeType وقمنا بتعريف 4 قيم ثابتة تحت هذا الإسم وهم Manager, Employee, Trainee, Volunteer
لاحظ أنك هنا تكتب الأنواع كأنها متغيرات وليست string فهنا كتبنا Manager وليس "Manager"

الأمر هنا يشبه الـ struct فهنا نحن أنشأنا نوع جديد من البيانات وهو EmployeeType وهذا النوع يحتوي على 4 قيم ثابتة داخله

الآن كل ما عليك فعله هو تعريف متغير من نوع EmployeeType

EmployeeType empType;

لاحظ أننا نستخدم الـ EmployeeType كنوع بيانات جديده مثله مثل int أو float أو double وهكذا
ثم أنشأنا منه متغير جديد بإسم empType من نوع EmployeeType

ميزة المتغير من نوع Enum أنه لن يقبل أي قيمة غير القيم الثابتة التي تم تعريفها في الـ Enum الذي أنشأناه منه
بمعنى أن المتغير empType طالما هو نوع EmployeeType فلن يقبل أي قيمة غير Manager, Employee, Trainee, Volunteer
وإذا أخطأت في كتابة القيمة فسينبهك وسيعطيك خطأ في الكود فورًا

empType = Manager; // ✅
empType = Employee; // ✅

empType = Manger; // ❌
empType = manager; // ❌
empType = Hamada; // ❌

قد تقول هل هذا يمكنه فعله مع الـ string أيضًا؟ سأقول لك أن مشكلة الـ string هي أنها تقبل أي قيمة تكتبها حتى ولو أخطأت في كتابتها

string empType;

empType = "Manager"; // ✅
empType = "Employee"; // ✅

empType = "Manger"; // ✅
empType = "manager"; // ✅
empType = "Hamada"; // ✅

هنا يمكنك أن ترى أن الـ string تقبل أي قيمة تكتبها حتى ولو كانت خاطئة، وهذا يعني أنك قد تخطئ في كتابة القيمة بالخطأ
أما الـ Enum فهو يحميك من هذا الخطأ ويعطيك قيم ثابتة لا يمكنك تغييرها وسينبهك إذا أخطأت في كتابتها

بعد أن قمنا بتعريف Enum وأسميناه EmployeeType وأنشأنا متغير من نوعه، يمكنك استخدامه في الشروط والدوال كأي متغير عادي

EmployeeType empType;

empType = Manager;

if (empType == Manager) {
    cout << "This is a Manager";
} else if (empType == Employee) {
    cout << "This is an Employee";
}

هنا قمنا بتعريف متغير empType من نوع EmployeeType وقمنا بتعيين قيمة Manager له
ثم قمنا بتحقق من قيمة المتغير empType وطباعة نص معين حسب القيمة التي يحملها المتغير
هذا هو الهدف من الـ Enum، تعريف مجموعة من القيم الثابتة تحت مسمى واحد


عندما تقوم بطباعة قيمة المتغير empType ستظهر لك القيمة بشكل رقمي وليس بشكل نصي
بمعنى أنه لن يظهر لك Manager بل سيظهر لك الرقم 0 لأن Manager هو القيمة الأولى في الـ Enum وهكذا

empType = Manager;
cout << empType; // 0

هذا بسبب أنه عندما تقوم بتعريف Enum فإنه يعطي لك قيم رقمية تبدأ من الصفر وتزيد بمقدار 1 لكل قيمة جديدة

enum EmployeeType {
    Manager, // 0
    Employee, // 1
    Trainee, // 2
    Volunteer // 3
};

هنا Manager يمثل القيمة 0 وEmployee تمثل القيمة 1 وهكذا
ويمكنك بالطبع تغيير هذه القيم الرقمية وتعيين أي قيمة رقمية تريد بشكل يدوي

enum EmployeeType {
    Manager = 1,
    Employee = 2,
    Trainee = 3,
    Volunteer = 4
};

هنا قمنا بتعيين قيم رقمية بشكل يدوي لكل قيمة، فManager يمثل القيمة 1 وEmployee تمثل القيمة 2 وهكذا

حل مسألة باستخدام الـ Enum

لنفترض أننا لدينا قاعدة بيانات صغيرة للموظفين ونريد تخزين نوع كل موظف فيها

enum EmployeeType {
    Manager,
    Employee,
    Trainee,
    Volunteer
};

struct Employee {
    int id;
    string name;
    EmployeeType type;
};

هنا قمنا بتعريف Enum بإسم EmployeeType وقمنا بتعريف 4 قيم ثابتة تحت هذا الإسم
ثم قمنا بتعريف struct بإسم Employee وهو يحتوي على 3 متغيرات id, name, type
ولاحظ أن المتغير type من نوع EmployeeType وهذا يعني أنه لن يقبل سوى القيم الثابتة التي تم تعريفها في الـ Enum

الآن يمكنا إنشاء قائمة بيانات للموظفين وتخزين نوع كل موظف فيها

Employee employees[10] = {
    {1, "Ahmed", Manager},
    {2, "Mohamed", Employee},
    {3, "Ali", Employee},
    {4, "Omar", Employee}
    {5, "Kamal", Employee},
    {6, "Salah", Trainee},
    {7, "Mahmoud", Trainee},
    {8, "Ashraf", Trainee}
    {9, "Khalid", Volunteer},
    {10, "Hamada", Volunteer}
};

هنا قمنا بإنشاء أراي من نوع Employee بإسم employees وقمنا بتعيين بيانات لكل موظف فيها
ولاحظ كيف أننا استخدمنا الـ Enum لتعريف نوع كل موظف بشكل واضح وسهل وبدون أخطاء إملائية لأنه كان سينبهنا فورًا

الآن ما رأيك بحل مسألة بسيطة باستخدام الـ Enum ؟

قم باحضار لي الأسماء الأشخاص الذين من نوع Employee و Volunteer فقط

for (int i = 0; i < 10; i++) {
    if (employees[i].type == Employee || employees[i].type == Volunteer) {
        cout << employees[i].name << '\n';
    }
}

هنا قمنا بعمل loop للمرور على عناصر الأراي employees ونقوم بالتحقق من نوع كل موظف
وإذا كان من نوع Employee أو Volunteer فسنقوم بطباعة اسمه

هذا هو الهدف من الـ Enum، تعريف مجموعة من القيم الثابتة تحت مسمى واحد واستخدامها في البرنامج بشكل سهل وواضح

تخيل لو استخدمنا int بدلاً من الـ Enum كيف سيكون الكود؟

كأن الشرط سيكون بهذا الشكل

if (employees[i].type == 1 || employees[i].type == 4) {
    cout << employees[i].name << '\n';
}

هل تستطيع أن تعرف ماذا يعني الرقم 1 والرقم 4 ؟
إذا كنت تعرف فهذا جيد، لكن هل ستتذكر هذه الأرقام دائمًا؟ بعد اسبوع أو شهر أو سنة؟

وماذا لو استخدمنا string بدلاً من الـ Enum ؟
كأن الشرط سيكون بهذا الشكل

if (employees[i].type == "Employee" || employees[i].type == "Volunter") {
    cout << employees[i].name << '\n';
}

أوه أنا أعتذر لقد في كتابة، لقد كتبت Volunter بدلاً من Volunteer
دعني أصححها الآن

if (employees[i].type == "Employee" || employees[i].type == "Volunteer") {
    cout << employees[i].name << '\n';
}

هل لاحظت الفرق بين الـ Enum والـ string ؟
الـ string قد تحتوي على أخطاء إملائية ولن يقوم بتنبيهك بل يجب عليك أنت أن تنتبه لها وقد تجد أن البرنامج لا ينفذ المطلوب بشكل صحيح لمجرد أنك كتبت Volunter بدلاً من Volunteer

أما الـ Enum فهو يحميك من هذه الأخطاء وسينبهك إذا أخطأت في كتابتها ولن يقبل سوى القيم الثابتة التي تم تعريفها في الـ Enum


يمكنك هنا بدلًا من الـ if أن تستخدم الـ switch لتحقق من نوع كل موظف وجعل الكود أكثر وضوحًا إذا أردت

for (int i = 0; i < 10; i++) {
    switch (employees[i].type) {
        case Employee:
        case Trainee:
            cout << employees[i].name << '\n';
    }
}

الخاتمة

الـ Enum أداة قوية ومفيدة في البرمجة تساعدك على تعريف مجموعة من القيم الثابتة تحت مسمى واحد
لتفادي الأخطاء الشائعة وأيضًا لتسهيل فهم الكود وتعامل معه ويجعله مقروء وواضح

فمثلًا هنا في الشرط السابق employees[i].type == Employee || employees[i].type == Volunteer
يمكنك أن تعرف بسهولة أننا نقوم بالتحقق من نوع الموظف إذا كان من نوع Employee أو Volunteer


الـ Enum بالطبع مفهوم تدعمه معظم لغات البرمجة ولكن الطريقة تختلف من لغة لأخرى

فمثلًا في لغة الـ Typescript يمكنك تعريف الـ Enum بنفس الطريقة الخاصة بالـ C++

enum EmployeeType {
  Manager,
  Employee,
  Trainee,
  Volunteer,
}

بصراحة معظم اللغات تدعم شكل الـ Enum بنفس طريقة الـ C++

أما في لغة php فالشكل يختلف قليلًا فقط نضيف case قبل كل نوع

enum EmployeeType {
  case Manager;
  case Employee;
  case Trainee;
  case Volunteer;
}

أتمنى أن تكون قد استفدت من هذه المقالة وأن تكون قد فهمت الـ Enum وكيفية استخدامه في البرمجة
وأن تكون قد فهمت الفائدة منه وكيف يمكنك من تجنب الأخطاء الشائعة وجعل الكود أكثر وضوحًا وسهولة