دلوقتي هنتكلم عن مكونات الـ Class. عشان نعرف Class، بنحط الـ Access Modifier، بعدين كلمة class، وبعدين اسم الكلاس (مثلاً Account لتمثيل حسابات العملاء في البنك)، وبنفتح الأقواس Scope عشان نعرف الـ Members جواه.

1. Field

الفيلد بيتعرف باننا بنكتب الـ Access Modifier، ثم الـ Data Type، ثم الـ Identifier. بعض الناس بتفضل تسمي الـ Field اللي نوعه Private بـ Underscore في البداية (زي _balance).

internal class Account
{
    // Best practice: Fields should be private for encapsulation
    private decimal _balance; 
}

نقطة مهمة: من الافضل دائمًا الـ Field يكون private عشان تحمي الحالة الداخلية (State) للكلاس، وتعمل Encapsulation سليم.

لو شلت الـ Access Modifier، الـ Default للـ Member بيكون private.

2. Constants

الـ Constant هو عضو قيمته بيتم إسنادها لأول مرة وتثبيتها وقت الـ Compilation (Initialization). القيمة دي لن تتغير تحت أي ظرف. مثال على كدة: الحد الأقصى للسحب اليومي.

// This value is immutable and cannot be changed anywhere in the code
public const decimal DailyCashLimit = 1000m;

3. Properties

لو عايز العالم الخارجي يتعامل مع الـ Field (اللي هو _balance) بس بشروطي، بستخدم الـ Property. الـ Property بتديني طريقة أعمل Access على Value.

Full Property: بتتكون من get (عشان القراءة) و set (عشان التخزين).

public decimal Balance
{
    get { return _balance; }
    set { _balance = value; }
}

Automatic Property: لو عمليتي القراءة والتخزين بيتموا بشكل مباشر بدون Logic إضافي، بنقدر نستغني عن الـ Syntax المعقد ونستخدم الـ Auto Property. الكومبايلر هنا بيعمل الـ Backing Field نيابة عنك “Under the hood”.

// Simplification syntax
public decimal Balance { get; set; }

4. Function Method

الـ Methods بتمثل السلوك (Behavior). ليها Syntax معين: Access Modifier، ثم الـ Return Type (أو void لو مابترجعش قيمة)، ثم اسم الـ Method (يفضل يكون فعل بيعبر عن وظيفتها زي Withdraw)، وأخيرًا الـ Parameter List.

public void Withdraw(decimal amount)
{
    // Logic to deduct amount from balance
    _balance -= amount;
}
 
public void Deposit(decimal amount)
{
    // Logic to add amount to balance
    _balance += amount;
}

في المثال ده، الـ Method بتستقبل قيمة amount وبتعدل على الـ Field الداخلي _balance.

5. Event

الـ Event هو آلية للـ Communication بين الـ Objects (مشتق من Observer Pattern). بيخلي Object معين يبلغ Objects تانيين إن “في حاجة حصلت عندي”، بدون ما يكشف تفاصيله الداخلية.

مثلاً، لو عملية السحب Withdraw خلت الرصيد بالسالب (Overdraft)، لازم نبلغ المهتمين بالحدث ده.

// Definition using EventHandler delegate
public event EventHandler OnOverDraft;
 
public void Withdraw(decimal amount)
{
    if (_balance < amount)
    {
        // Trigger the event: (Sender Object, Arguments)
        this.OnOverDraft(this, null);
    }
    else
    {
        _balance -= amount;
    }
}

أي شخص مهتم بالحدث ده يقدر يشترك (Subscribe) ويعمل الـ Logic الخاص بيه لما الحدث يقع.

6. Operator

بمبدأ الـ Operator Overloading، بنقدر نخلي الـ Custom Type (زي Account) يتعامل مع العمليات الحسابية زي الجمع والطرح. طبيعي إننا نجمع أرقام، بس عشان نجمع “حسابين بنكيين”، لازم نفهم الكومبايلر يعني إيه علامة + بين كلاس Account.

// Overloading the '+' operator to add balances of two accounts
public static Account operator +(Account a, Account b)
{
    return new Account { Balance = a.Balance + b.Balance };
}

بالشكل ده، تقدر في أي مكان في الكود تكتب account1 + account2.

7. Constructor

الـ Constructor هو Method خاصة، ليها نفس اسم الكلاس، وبتتنادى أول ما يتعمل Creation للـ Object. عندنا نوعين:

  1. الـ Default Constructor: ملهوش Parameters (الكومبايلر بيعمله لوحده لو إنت ما كتبتهوش).
  2. الـ Parameterized Constructor: بنستخدمه عشان نعمل Initialization بقيم أولية.
public Account(decimal initialBalance)
{
    _balance = initialBalance;
}

8. Finalizers (Destructor)

عضو خاص بيشبه الـ Method بس اسمه بيبدأ بـ Tilde (~). ملهوش Parameters. بيستخدم لعمليات التنظيف (Cleanup) للـ Unmanaged Resources (زي File Handlers أو Network Connections).

  • مش إنت اللي بتناديه، الـ Garbage Collector (GC) هو اللي بيناديه.
  • تحذير: مفيش ضمان لإمتى هيتنادى، عشان كدة لا يوصى بالاعتماد عليه في الـ Clean Code الحديث، ويفضل استخدام طرق تانية (زي IDisposable).
~Account()
{
    // Cleanup code here
}

9. Indexer

الـ Indexer بيخليك تتعامل مع الـ Object وكأنه Array. لو عندنا كلاس اسمه JointAccount (حساب مشترك) بيشيل جواه أكتر من حساب، عشان أوصل لحساب معين جواه باستخدام الـ Index [0]، لازم أعرف Indexer.

// Using 'this' keyword for indexer
public Account this[int index]
{
    get
    {
        // Return account based on index logic
        return _accountList[index];
    }
    set
    {
        // Set account logic
        _accountList[index] = value;
    }
}

10. Nested Types

أحيانًا بنحتاج نعرف Type جوه Type تاني (زي PrimaryAccount جوه JointAccount) لو الـ Business Logic بيتطلب ده.

  • الـ Nested Class بيتمتع بصلاحيات وصول خاصة.
  • مسموح نستخدم معاه private، protected، وغيرها.
  • نصيحة Clean Code: لو قررت تعمل الكلاس داخلي، خليه مخفي (Private/Internal) ولا تجعله مرئي للعالم الخارجي إلا للضرورة القصوى، تجنبًا لتعقيد الكود.

Conclusion

دول الـ 10 أعضاء للكلاس. كل واحد ليه صيغة مميزة واستخدام محدد عند الحاجة. مش لازم تستخدمهم كلهم، بس استخدم اللي بيوفرلك الحل الأمثل لمشكلتك، وتجنب عمل “God Object” (كلاس واحد بيعمل كل حاجة) لأنه ضد مبادئ الكود النظيف.


Notes

حاجات جديدة في .NET 9 و 10

Primary Constructor

بدل ما نعرف الـ Constructor بالطريقة التقليدية وتكرار الكود، .NET الحديث قدم الـ Primary Constructors للـ Classes والـ Structs (كانت موجودة للـ Records بس).

الطريقة القديمة:

class Account
{
    private decimal _balance;
    public Account(decimal initialBalance)
    {
        _balance = initialBalance;
    }
}

الطريقة الحديثة (Primary Constructor):

class Account(decimal initialBalance) // Parameter directly on class
{
    private decimal _balance = initialBalance;
}

Required Properties

عشان نجبر الـ Developer إنه يعمل Initialize لـ Properties معينة وقت إنشاء الـ Object (بدون ما نعمل Constructor كبير)، ظهرت كلمة required.

public required string AccountNumber { get; set; }

لو جيت تعمل new Account() من غير ما تدي قيمة لـ AccountNumber، الكومبايلر هيديك Error.

Finalizers

في بيئات .NET الحديثة (Core / 5+ / 10)، الاعتماد على الـ Finalizers (~ClassName) أصبح نادر جداً وغير مستحب إلا لو بتكتب Low-level code بيتعامل مع Unmanaged APIs مباشرة. النمط المعتمد الآن هو استخدام IDisposable interface و SafeHandle.