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

التعداد Enum في C#: القيم وToString وParse وFlags والمرور

كيف تعمل التعدادات في C#: التصريح بثوابت مسمّاة، والقيم الصحيحة الأساسية والتحويل، وتحويل التعداد إلى نص والنص إلى تعداد بـ Parse وTryParse، وسرد كل القيم، و[Flags] مع المعاملات البتّية وHasFlag، والتفرّع على تعداد، ومعالجة القيم غير المعرّفة.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

التعداد (enum) نوع قيمه مجموعة ثابتة من الثوابت المسمّاة: حالات الطلب، أيام الأسبوع، مستويات السجل. في الأساس كل اسم عدد صحيح، لكن نظام الأنواع يمنع خلط OrderStatus مع int عادي أو مع تعداد آخر.

التصريح بتعداد واستخدامه

اسرد أسماء الأعضاء بين أقواس معقوصة. افتراضيًا يكون الأول 0 وكل تالٍ أعلى بواحد:

المخرجات:

Paid
On its way
True
2

التعداد نوع حقيقي: لا يمكن استدعاء دالة تأخذ OrderStatus بالقيمة 3 أو بـ LogLevel خطأً. التعدادات أنواع قيم، فلا تكون null أبدًا وتُقارن بـ == بالقيمة.

القيم الصريحة والنوع الأساسي

يمكنك إسناد الأرقام بنفسك. وهذا مهم كلما غادر الرقم برنامجك (عمود قاعدة بيانات، حالة HTTP، صيغة ملف)، لأن إعادة الترقيم عندها تكسر البيانات المخزّنة:

المخرجات:

404
Created
418
1
Byte

أمران يجب ملاحظتهما. تحويل int إلى تعداد لا يفشل أبدًا: (HttpStatus)418 قيمة صالحة لا اسم لها فحسب، وتُطبع كرقم. ويمكن أن يكون النوع الأساسي أي نوع صحيح (byte وshort وlong، ...)، وهذا لا يهمّ إلا في الشيفرة الحساسة للتخزين؛ وint هو الافتراضي والخيار الصحيح في الغالب دائمًا.

حين تضيف أعضاء لاحقًا أضفها في النهاية أو أعطها قيمًا صريحة. إدراج Refunded بين Paid وShipped يغيّر بصمت رقم كل عضو بعده.

من التعداد إلى نص

تعيد ToString() اسم العضو، وهو أيضًا ما تستخدمه Console.WriteLine واستيفاء النصوص. وتغيّر نصوص التنسيق المخرجات:

المخرجات:

Warning
2
00000002
[Warning]
Error
Error
Needs attention

أسماء الأعضاء معرّفات، فلا يمكن أن تحتوي على مسافات ولا تُترجم. للنص المعروض على المستخدمين اربط القيم بنفسك، كما تفعل Label، أو بـ Dictionary<LogLevel, string>. بعض قواعد الشيفرة تضع سمة [Description("Needs attention")] على كل عضو وتقرؤها بالانعكاس؛ وتُظهر صفحة الانعكاس والسمات كيف يعمل ذلك البحث.

من النص إلى التعداد: Parse وTryParse

تحوّل Enum.Parse اسمًا إلى قيمة وترمي ArgumentException إن لم يطابق شيء. وتعيد Enum.TryParse القيمة false بدلًا من ذلك، وهذا ما تريده لأي مدخلات لا تتحكم فيها:

المخرجات:

Large
Medium
Parse threw ArgumentException for Huge
small  parsed=True  value=Small  defined=True
XL     parsed=False value=Small  defined=True
2      parsed=True  value=Large  defined=True
7      parsed=True  value=7      defined=False

الصفّان الأخيران هما الفخ. كلتا الدالتين تقبل النصوص الرقمية، فتُقرأ "7" بنجاح إلى Size لا اسم لها. وTryParse الفاشلة تضبط النتيجة على 0، وهي هنا Small التي تبدو صالحة. حين يأتي النص من سلسلة استعلام أو ملف إعدادات أو نموذج فافحص دائمًا القيمة المعادة وEnum.IsDefined كليهما:

if (Enum.TryParse(input, true, out Size size) && Enum.IsDefined(typeof(Size), size))
{
    // safe to use size
}

تضيف .NET Core 2.0 وما بعدها النسخة العامة Enum.Parse<Size>("Large") التي لا تحتاج إلى تحويل.

سرد كل القيم

تعيد Enum.GetValues كل عضو، مرتّبة بالقيمة الرقمية (مقارنة بلا إشارة، فتأتي الأعضاء السالبة أخيرًا)؛ وتعيد Enum.GetNames أسماءها. هكذا تملأ قائمة منسدلة أو تتحقق مقابل كل خيار:

المخرجات:

Free       0 EUR/month
Starter    9 EUR/month
Pro       29 EUR/month
Team      99 EUR/month
Free | Starter | Pro | Team
3 paid plans

تعيد Enum.GetValues(typeof(Plan)) مصفوفة Array عادية، ومن هنا Cast<Plan>() قبل LINQ. وفي .NET 5 وما بعده تعيد Enum.GetValues<Plan>() مصفوفة Plan[] مباشرة.

الأعلام Flags: دمج القيم

بعض التعدادات تصف مجموعة خيارات لا خيارًا واحدًا: أذونات الملفات، أيام فتح متجر، قنوات الإشعارات. أعطِ كل عضو بتًّا خاصًا به (1 و2 و4 و8، ...)، وأضف None = 0، وعلّم التعداد بـ [Flags]. عندها تُدمج القيم بـ |:

المخرجات:

Read, Share
Editor, Share
True
False
Editor
3
Read, Delete
True

ما يفعله كل معامل: تضبط | البتات، وتمسحها & ~X، وتقلبها ^، وتختبرها (value & X) != 0 أو value.HasFlag(X). تعني HasFlag(X) أن "كل بتات X مضبوطة"، فتكون HasFlag(None) صحيحة لكل قيمة، وتتطلب HasFlag(Editor) كلًّا من Read وWrite.

لاحظ السطر الثاني: حين يغطي تركيب مسمّى بعض البتات المضبوطة تستخدمه ToString، فتُطبع Read | Write | Share كـ Editor, Share. تذكّر ذلك قبل قراءة مخرجات ToString بأي شيء غير Enum.Parse.

السمة لا تغيّر الحساب. بل تغيّر التنسيق: دون [Flags] تُطبع Read | Share كـ 9، لأنه لا عضو واحد له تلك القيمة. ومعها تعمل ToString وParse كلتاهما بالشكل المفصول بفواصل. ويجب أن تظل الأعضاء قوى للعدد 2؛ كتابة Read, Write, Delete بالترقيم الافتراضي (0 و1 و2) تجعل Write | Delete تساوي 3، وهي قيمة بلا معنى.

التفرّع على تعداد

switch هي الطريقة الطبيعية للتصرف بحسب تعداد. ضمّن فرع default، لأن متغيّر التعداد قد يحمل قيمًا بلا اسم:

switch (status)
{
    case OrderStatus.Pending:
    case OrderStatus.Paid:
        return "Preparing";
    case OrderStatus.Shipped:
        return "On the way";
    case OrderStatus.Delivered:
        return "Delivered";
    default:
        return "Unknown";
}

ومنذ C# 8 يكون تعبير switch أقصر. دون ذراع _ يحذّر المترجم: CS8509 حين يغيب عضو مسمّى، وCS8524 حين يُعالج كل اسم لكن لا تُعالج القيم غير المسمّاة مثل (OrderStatus)7:

string text = status switch
{
    OrderStatus.Pending or OrderStatus.Paid => "Preparing",   // 'or' pattern: C# 9
    OrderStatus.Shipped => "On the way",
    OrderStatus.Delivered => "Delivered",
    OrderStatus.Cancelled => "Cancelled",
    _ => throw new ArgumentOutOfRangeException(nameof(status)),
};

القيم الافتراضية وغير المعرّفة

القيمة الافتراضية لأي تعداد 0، سواء كان لعضو تلك القيمة أم لا. تنتجها الحقول وعناصر المصفوفات وTryParse الفاشلة. صمّم لذلك:

  • اجعل 0 عضوًا ذا معنى "غير مضبوط" (None أو Unknown) بدل خيار حقيقي. وإلا يُقرأ الحقل غير المهيّأ بصمت كأول خيار حقيقي.
  • تحقّق من الأرقام الآتية من الخارج بـ Enum.IsDefined. وفي تعدادات [Flags] تعيد IsDefined القيمة false للتركيبات التي لا اسم لها (Read | Share)، فافحص البتات بدلًا من ذلك: (value & ~Permissions.All) == 0 مع عضو All يغطي كل بت.

أخطاء شائعة

  • الثقة بـ TryParse وحدها. النصوص الرقمية تُقرأ، والقراءة الفاشلة تعطي 0. أضف Enum.IsDefined.
  • الاعتماد على الترقيم الضمني للقيم المخزّنة. إدراج عضو يعيد ترقيم ما بعده. أسند قيمًا صريحة لأي تعداد يُحفظ.
  • أعلام دون قوى العدد 2. الترقيم الافتراضي (0 و1 و2 و3) يداخل البتات. استخدم 1 و2 و4 و8، أو 1 << n.
  • عرض ToString() على المستخدمين. أسماء الأعضاء معرّفات شيفرة. اربط القيم بنص عرض.
  • لا default في switch. يمكن للتعداد أن يحمل قيمًا خارج أعضائه المسمّاة.

الأسئلة الشائعة

كيف أحوّل تعدادًا إلى نص في C#؟

استدعِ ToString(): تعيد OrderStatus.Shipped.ToString() النص "Shipped"، ويفعل استيفاء النصوص الشيء نفسه. وتعطي ToString("D") الرقم بدلًا منه. ولاسم معروف وقت الترجمة تكون nameof(OrderStatus.Shipped) ثابتًا. أما للنص الموجّه للمستخدم بمسافات أو ترجمات فاربط القيم بالنصوص بنفسك (بـ switch أو قاموس) بدل الاعتماد على اسم العضو.

كيف أحوّل نصًا إلى تعداد في C#؟

استخدم Enum.TryParse<OrderStatus>(text, true, out var status)، التي تعيد false بدل رمي استثناء حين لا يطابق النص أي عضو (تجعلها true غير حساسة لحالة الأحرف). وترمي Enum.Parse(typeof(OrderStatus), text) استثناء ArgumentException مع المدخلات السيئة. وكلتاهما تقبل أيضًا نصوصًا رقمية مثل "42"، فافحص النتيجة بـ Enum.IsDefined حين تأتي المدخلات من المستخدمين.

كيف أحوّل بين تعداد وint في C#؟

حوّل صراحة في أي اتجاه: int code = (int)OrderStatus.Paid; وvar status = (OrderStatus)2;. التحويل من int لا يفشل أبدًا، حتى للأرقام التي لا عضو مطابقًا لها؛ والنتيجة قيمة تعداد تُطبع كرقم. تحقّق بـ Enum.IsDefined(typeof(OrderStatus), value) حين يأتي الرقم من الخارج.

كيف أمرّ على كل قيم تعداد في C#؟

تزور foreach (OrderStatus s in Enum.GetValues(typeof(OrderStatus))) كل عضو بترتيب قيمه الرقمية. ومنذ .NET 5 توجد نسخة عامة، Enum.GetValues<OrderStatus>()، لا تحتاج إلى تحويل. وتعيد Enum.GetNames(typeof(OrderStatus)) الأسماء كنصوص.

ماذا تفعل [Flags] على تعداد في C#؟

تعلّم تعدادًا قيمه بتات مقصود دمجها بـ |، مثل Read | Write. أعطِ كل عضو قوة من قوى العدد 2 (1 و2 و4 و8) وعضوًا None = 0. تجعل السمة ToString() تطبع التركيبات بالشكل "Read, Write" وتتيح لـ Enum.Parse قراءة هذا الشكل. اختبر بتًّا بـ HasFlag أو (value & Permissions.Write) != 0.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن