C#: القوائم والهيكلية

1. القوائم (enum)

التعداد (enum) هو نوع قيمة يُستخدم لتعريف مجموعة من الثوابت المسماة، مما يجعل الكود أكثر قابلية للقراءة وأكثر أمانًا من حيث الأنواع.

(1) تعريف قائمة التعداد

استخدم الكلمة الرئيسية enum لتعريف قائمة التعداد. النوع الأساسي الافتراضي هو int، حيث تبدأ القيم من 0 وتتزايد.

▶ مثال

CSHARP
enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

class Program
{
    static void Main()
    {
        Season current = Season.Autumn;
        System.Console.WriteLine(current);
        System.Console.WriteLine((int)current);
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Autumn
2

(2) النوع الأساسي والتعيين الصريح

يُعيَّن نوع «enum» الأساسي افتراضيًّا إلى int. يمكنك تحديد نوع صحيح آخر بشكل صريح وتعيين قيم محددة للعناصر.

▶ مثال

CSHARP
enum Priority : byte
{
    Low = 1,
    Medium = 2,
    High = 3
}

enum HttpStatusCode
{
    OK = 200,
    NotFound = 404,
    InternalServerError = 500
}

class Program
{
    static void Main()
    {
        Priority p = Priority.High;
        System.Console.WriteLine((byte)p);
        System.Console.WriteLine((int)HttpStatusCode.NotFound);
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
3
404

(3) تحويل القيم المُعددّة

يمكن تحويل أنواع التعدادات والأنواع الأساسية المرتبطة بها بشكل صريح من نوع إلى آخر.

▶ مثال

CSHARP
enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

class Program
{
    static void Main()
    {
        Season s = Season.Spring;
        int value = (int)s;
        System.Console.WriteLine(value);

        Season fromInt = (Season)2;
        System.Console.WriteLine(fromInt);
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
0
Autumn

2. طرق فئة Enum

توفر فئة System.Enum عدة طرق ثابتة لإجراء عمليات على القوائم التعدادية.

(1) الطرق الشائعة

الطريقة الوصف
Enum.Parse يحول سلسلة إلى قائمة تعدادية؛ ويرمي استثناءً في حالة الفشل
Enum.TryParse يحول سلسلة إلى قيمة من قائمة التعداد؛ ويعيد القيمة «false» في حالة الفشل
Enum.GetName يحصل على اسم قيمة من قائمة التعداد
Enum.GetValues يحصل على جميع قيم التعداد
Enum.IsDefined يحدد ما إذا كانت القيمة موجودة في قائمة التعداد

▶ مثال

CSHARP
enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

class Program
{
    static void Main()
    {
        Season parsed = (Season)Enum.Parse(typeof(Season), "Summer");
        System.Console.WriteLine(parsed);

        bool ok = Enum.TryParse<Season>("Winter", out Season result);
        System.Console.WriteLine($"{ok}, {result}");

        string name = Enum.GetName(typeof(Season), 2);
        System.Console.WriteLine(name);

        foreach (Season s in Enum.GetValues(typeof(Season)))
        {
            System.Console.WriteLine($"{s} = {(int)s}");
        }

        System.Console.WriteLine(Enum.IsDefined(typeof(Season), 1));
        System.Console.WriteLine(Enum.IsDefined(typeof(Season), 99));
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Summer
True, Winter
Autumn
Spring = 0
Summer = 1
Autumn = 2
Winter = 3
True
False

3. الهياكل (struct)

البنية (struct) هي نوع قيمة مناسب لتغليف كميات صغيرة من البيانات. يتم تخزين البنى في المكدس، وتؤدي عملية التعيين إلى نسخ القيمة بأكملها.

(1) تعريف بنية (Struct)

استخدم الكلمة الرئيسية struct لتعريف بنية.

▶ مثال

CSHARP
struct Point
{
    public int X;
    public int Y;

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public override string ToString()
    {
        return $"({X}, {Y})";
    }
}

class Program
{
    static void Main()
    {
        Point p1 = new Point(3, 4);
        Point p2 = p1;
        p2.X = 10;
        System.Console.WriteLine(p1);
        System.Console.WriteLine(p2);
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
(3, 4)
(10, 4)

4. البنية مقابل الفئة

توجد اختلافات مهمة بين الهياكل (Structs) والفئات (Classes) من حيث الدلالة والسلوك.

(1) المقارنة

خاصية بنية فئة
فئة النوع نوع القيمة نوع المرجع
موقع التخزين المكدس الركام
سلوك التعيين نسخ القيمة نسخ المرجع
التوريث غير مدعوم (لا يمكن توريثه) مدعوم
الواجهات يمكن تنفيذها يمكن تنفيذها
مُدمِّر لا شيء نعم
القيمة الافتراضية جميع الحقول بقيمة صفر null

▶ مثال

CSHARP
struct PointStruct
{
    public int X;
    public int Y;
}

class PointClass
{
    public int X;
    public int Y;
}

class Program
{
    static void Main()
    {
        PointStruct s1 = new PointStruct();
        PointStruct s2 = s1;
        s2.X = 5;
        System.Console.WriteLine($"struct: s1.X={s1.X}, s2.X={s2.X}");

        PointClass c1 = new PointClass();
        PointClass c2 = c1;
        c2.X = 5;
        System.Console.WriteLine($"class: c1.X={c1.X}, c2.X={c2.X}");
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
struct: s1.X=0, s2.X=5
class: c1.X=5, c2.X=5

5. متى تُستخدم الهياكل (Structs)

لا تُعد البنيات (structs) دائمًا أفضل من الفئات (classes). يُنصح باستخدام البنية في الحالات التالية:

أمثلة شائعة: Point، Color، DateTime، TimeSpan.

⚠️ في حالة عدم استيفاء الشروط المذكورة أعلاه، استخدم فئة لتجنب انخفاض الأداء الناتج عن عمليات النسخ المتكررة.


6. البنية readonly (C# 7.2+)

يضمن readonly struct أن تكون جميع الحقول غير قابلة للتعديل. ويمنع المُجمِّع إجراء أي عمليات تعديل.

▶ مثال

CSHARP
readonly struct Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public double DistanceTo(Point other)
    {
        int dx = X - other.X;
        int dy = Y - other.Y;
        return System.Math.Sqrt(dx * dx + dy * dy);
    }
}

class Program
{
    static void Main()
    {
        Point p1 = new Point(3, 4);
        Point p2 = new Point(0, 0);
        System.Console.WriteLine(p1.DistanceTo(p2));
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
5

7. بنية «record» (C# 10+)

يجمع record struct بين دلالات القيم الخاصة بالـ«struct» ومساواة القيم والصيغة الموجزة الخاصة بالـ«record»، وهو مناسب للحالات التي تتطلب عدم قابلية التغيير وعناصر يتم إنشاؤها تلقائيًا.

▶ مثال

CSHARP
public class Point : IEquatable<Point>
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
    public Point With(int? x = null, int? y = null)
    {
        return new Point(x ?? this.X, y ?? this.Y);
    }
    public bool Equals(Point other)
    {
        return other != null && X == other.X && Y == other.Y;
    }
    public override bool Equals(object obj) => Equals(obj as Point);
    public override int GetHashCode() => System.HashCode.Combine(X, Y);
    public override string ToString() => $"Point {{ X = {X}, Y = {Y} }}";
}

class Program
{
    static void Main()
    {
        Point p1 = new Point(1, 2);
        Point p2 = new Point(1, 2);
        System.Console.WriteLine(p1 == p2);
        System.Console.WriteLine(p1.With(x: 10));
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
True
Point { X = 10, Y = 2 }
💡 يقوم record struct بإنشاء تعبيرات Equals وToString وwith وغيرها من العناصر تلقائيًّا، مما يقلل من الكود النمطي.


❓ أسئلة شائعة

س هل يمكن أن تحدد قائمة التعداد (enum) نوعًا غير صحيح؟
ج لا، لا يمكن تحديد سوى الأنواع الصحيحة (byte، sbyte، short، ushort، int، uint، long، ulong).
س هل يمكن أن تحتوي البنية (struct) على مُنشئ؟
ج نعم، يمكن أن تحتوي على مُنشئات ذات معلمات، لكن المُنشئ الخالي من المعلمات يتم توفيره تلقائيًا بواسطة المُترجم ولا يمكن تعريفه بشكل مخصص.
س هل يمكن للبنية (struct) أن تنفذ واجهات؟
ج نعم، يمكن للبنى تنفيذ الواجهات. ويحدث «التعبئة» (boxing) عندما يتم تحويل بنية إلى نوع واجهة (تحويل من القيمة إلى المرجع).
س هل يمكن استخدام الأمر set مع حقول البنية القابلة للقراءة فقط؟
ج لا، يجب أن تكون جميع حقول البنية القابلة للقراءة فقط قابلة للقراءة فقط، ولا يمكن تهيئتها إلا من خلال المنشئ.
س ما الفرق بين enum و const؟
ج enum هي مجموعة من الثوابت المسماة التي تتمتع بسلامة الأنواع؛ أما const فهي ثابت واحد في وقت التحويل البرمجي ولا تتمتع بقدرة تجميع الأنواع.

📖 ملخص

📝 تمارين

  1. عرّف enum Direction { North, East, South, West }، واكتب كودًا للتكرار عبر جميع الاتجاهات وإخراج الاسم والقيمة الصحيحة
  2. عرّف متغيرًا struct Rectangle بعرض وارتفاع، وقدم طريقة لحساب المساحة، وتأكد من أن عملية التعيين تنسخ القيمة
  3. قم بتغيير Rectangle من التمرين السابق إلى readonly struct، مع التأكد من أن جميع الحقول هي للقراءة فقط وأنها تم تهيئتها من خلال مُنشئ الكائن
  4. استخدم Enum.TryParse وEnum.IsDefined لكتابة دالة آمنة لتحليل قيم التعداد (enum)، بحيث تتلقى سلسلة نصية وتُرجع قيمة التعداد المطابقة لها أو القيمة الافتراضية
  5. اكتب كودًا لمقارنة الفرق في السلوك بين struct وclass عند تمريرهما كمعلمات للطريقة (التمرير بالقيمة مقابل التمرير بالمرجع)
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%