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

الأصناف والكائنات في C#: الحقول والدوال وnew والمراجع

كيف تصرّح بصنف في C#، وتمنحه حقولًا ودوالًا، وتنشئ كائنات بـ new، وما معنى أن الصنف نوع مرجعي: الكائنات المشتركة، وnull وNullReferenceException، وToString ومهيّئات الكائنات.

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

الصنف (class) نوع تعرّفه: يجمع البيانات (الحقول) مع الشيفرة التي تعمل على تلك البيانات (الدوال). والكائن (object) مثيل واحد من صنف، يُنشأ وقت التشغيل بـ new. صنف واحد، وكائنات كثيرة، لكل منها قيمه الخاصة.

التصريح بصنف وإنشاء الكائنات

يسرد تصريح الصنف أعضاءه داخل أقواس معقوصة. تحمل الحقول البيانات، وتحمل الدوال السلوك. تحجز new ClassName() كائنًا وتعطيك مرجعًا إليه.

المخرجات:

Maya: 120.50
Omar: 40
Withdraw 200 succeeded: False

لكل كائن Owner وBalance خاصان به. داخل دالة تعني Balance "رصيد الكائن الذي استُدعيت عليه هذه الدالة"، ولهذا تغيّر maya.Deposit وomar.Deposit رقمين مختلفين. وذلك الكائن الضمني متاح باسم this حين تحتاج إلى تسميته.

الحقول التي لا تُسند إليها تبدأ بقيمتها الافتراضية: 0 للأرقام، وfalse لـ bool، وnull للنصوص وأنواع الأصناف الأخرى.

الأعضاء هنا public كي تصل إليها Main. أما الأصناف الحقيقية فتبقي الحقول عادة private وتكشف خصائص بدلًا منها، وفي صفحة معدّلات الوصول القواعد. ويمكن للصنف أيضًا التصريح بمُنشئ يضبط حقوله حين يُنشأ الكائن، وهو مشروح في صفحة المُنشئات.

الصنف نوع مرجعي

متغيّر من نوع صنف لا يحتوي على الكائن. بل يحتوي على مرجع إلى كائن يعيش في الكومة. إسناد المتغيّر ينسخ المرجع، فيمكن لمتغيّرين الإشارة إلى الكائن نفسه.

المخرجات:

5
6
True
False

تظهر هنا ثلاث نتائج:

  • تغيير الكائن عبر second مرئي عبر first، لأنه لا يوجد إلا كائن واحد.
  • تمرير كائن إلى دالة يمرّر المرجع. تستطيع الدالة تغيير حقول الكائن ويرى المستدعي ذلك. (إسناد كائن جديد إلى المعامل داخل الدالة لا يؤثر في متغيّر المستدعي؛ فهذا يحتاج إلى ref.)
  • تقارن == بين مثيلي صنف المراجع افتراضيًا: "هل هذا الكائن نفسه؟"، لا "هل لهما القيم نفسها؟". لـ lookalike بيانات متطابقة وما زال كائنًا مختلفًا.

إن أردت نسخًا عند الإسناد ومقارنة بالقيمة فانظر إلى البنى، وهي أنواع قيم، أو السجلات، التي تقارن بالقيمة.

null وNullReferenceException

يمكن لمتغيّر مرجعي أن يحمل null، أي "لا كائن". استخدام عضو عبر null يرمي NullReferenceException، وهو أشيع استثناء وقت تشغيل في شيفرة C#.

المخرجات:

True
no city on file
NullReferenceException: Address was never set
Lisbon

الإصلاح لا يكون تقريبًا بالتقاط الاستثناء. اكتشف لماذا المرجع null ثم إما أنشئ الكائن أبكر، أو افحص قبل الاستخدام، أو استخدم المعامل الشرطي لـ null ?.، الذي يتوقف ويعطي null بدل رمي استثناء. ثم تقدّم ?? قيمة بديلة. المعاملان كلاهما مشروحان في الأنواع القابلة لـ null.

تجاوز ToString

كل صنف يرث من object، الذي يوفّر ToString(). تعيد النسخة الموروثة الاسم الكامل للنوع (مع فضاء أسمائه إن وُجد)، ونادرًا ما يكون هذا ما تريد طباعته. تجاوزها:

المخرجات:

Plain
Mug (8.50)
In cart: Mug (8.50)
Total line: Mug (8.50)

تستدعي Console.WriteLine واستيفاء النصوص و+ مع نص كلها ToString() عنك. ويعرضها المصحّح أيضًا، وهذا يجعل تجاوزها مفيدًا حتى حين لا تطبع الكائن بنفسك أبدًا.

مهيّئات الكائنات

يضبط مهيّئ الكائن الحقول والخصائص العامة مباشرة بعد إنشاء الكائن، في التعبير نفسه. يُقرأ أفضل من قائمة إسنادات ويعمل جيدًا داخل القيم الحرفية للمجموعات.

المخرجات:

A12: 25 (0 extras)
A13: 40 (2 extras)

تفصيلان في التذكرة الثانية. تستبدل Price = 40m قيمة مهيّئ الحقل 25m، لأن مهيّئ الكائن يعمل بعد بناء الكائن. وExtras = { "Parking", "Poster" } بلا new: تستدعي Add على القائمة التي يحملها الحقل أصلًا، بدل استبدالها.

مهيّئ الكائن ليس مُنشئًا. لا يستطيع إلا ضبط الأعضاء التي يمكن للشيفرة المستدعية الوصول إليها، ولا يستطيع فرض ضبط عضو إطلاقًا. حين يكون الكائن غير صالح دون قيم معينة فاطلبها في مُنشئ.

الأصناف الجزئية

تقسم الكلمة partial صنفًا واحدًا على عدة ملفات. ويدمج المترجم الأجزاء في صنف واحد.

// Invoice.cs
public partial class Invoice
{
    public decimal Subtotal;
    public decimal Total() => Subtotal + Tax();
}

// Invoice.Tax.cs
public partial class Invoice
{
    private decimal Tax() => Subtotal * 0.2m;
}

يجب أن يقول كل جزء partial، وأن يكون في فضاء الأسماء نفسه والمشروع نفسه. الاستخدام الشائع هو الشيفرة المولَّدة: مصمّمات WinForms ومولّدات المصدر وبعض أدوات ORM تكتب جزءًا وتترك الآخر لك، فلا تكتب إعادة التوليد فوق شيفرتك أبدًا. أما تقسيم صنف مكتوب يدويًا على ملفات فقط لجعله أقصر فيخفي عادة صنفًا يجب أن يكون صنفين.

أخطاء شائعة

  • استخدام كائن قبل إنشائه. حقل من نوع صنف يكون null حتى تسند إليه new .... أول وصول إلى عضو يرمي NullReferenceException.
  • توقّع أن ينسخ الإسناد. تعطي var b = a; لصنف اسمًا ثانيًا للكائن نفسه. أنشئ كائنًا جديدًا وانسخ القيم حين تحتاج إلى كائن مستقل.
  • مقارنة الكائنات بـ == وتوقّع مقارنة بالقيمة. في الأصناف تقارن المراجع ما لم يحمّل الصنف == تحميلًا زائدًا (كما يفعل string). قارن الحقول المعنية، أو تجاوز Equals.
  • طباعة كائن والحصول على اسم نوعه. تجاوز ToString().
  • التصريح بكل شيء public. يعمل في الأمثلة الصغيرة، لكنه يتيح لأي شيفرة وضع الكائن في حالة غير صالحة، مثل رصيد سالب. أبقِ الحقول خاصة وغيّرها عبر دوال تفحص القواعد.

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

ما هو الصنف (class) في C#؟

الصنف نوع تعرّفه: مجموعة مسمّاة من البيانات (الحقول والخصائص) والسلوك (الدوال). يصرّح class Account { public decimal Balance; public void Deposit(decimal amount) { Balance += amount; } } بواحد. الصنف مخطّط؛ لا يفعل شيئًا حتى تنشئ منه كائنات بـ new.

ما الفرق بين الصنف والكائن في C#؟

الصنف هو التعريف، يُكتب مرة واحدة في الشيفرة المصدرية. والكائن مثيل واحد منه، يُنشأ وقت التشغيل بـ new، وله نسخته الخاصة من كل حقل مثيل. استدعاء new Account() مرتين يعطي كائنين من الصنف نفسه برصيدين مستقلين.

كيف تنشئ كائنًا في C#؟

استخدم new يليها اسم الصنف ووسائط المُنشئ: var account = new Account();. يمكنك ضبط الحقول والخصائص العامة في التعبير نفسه بمهيّئ كائن: new Account { Owner = "Maya" }. يحمل المتغيّر مرجعًا إلى الكائن الجديد، لا الكائن نفسه.

لماذا أحصل على NullReferenceException في C#؟

استخدمت عضوًا (.Name أو .Length أو استدعاء دالة) عبر متغيّر يحمل null، فلا كائن تعمل عليه. من الأسباب الشائعة حقل لم يُسند إليه قط (حقول الأصناف تبدأ null)، أو بحث أعاد null، أو دالة أعادت null عند الفشل. افحص بـ if (x != null)، أو استخدم x?.Name، أو تأكّد من إنشاء الكائن قبل الاستخدام.

كيف أتجاوز ToString في C#؟

صرّح بـ public override string ToString() في صنفك وأعد النص الذي تريده، مثل public override string ToString() => $"{Name} ({Price:F2})";. ودونها تطبع Console.WriteLine(obj) واستيفاء النصوص اسم الصنف، لأن هذا ما تعيده object.ToString().

Coddy programming languages illustration

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

ابدأ الآن