كل مطوّر C# يقابل System.NullReferenceException بشكل شبه يومي — الرسالة
الشهيرة "Object reference not set to an instance of an object" التي لا
تسمّي المذنب صراحة. فهم سببها الحقيقي مرة واحدة يوفّر عليك ساعات تخمين لاحقًا.
ماذا يعني الخطأ أصلًا؟
أي متغيّر من نوع مرجعي (class، مصفوفة، أو واجهة) هو مرجع يشير إلى مكان في
الذاكرة. null يعني أن هذا المرجع لا يشير إلى أي شيء بعد:
string? name = null;
int len = name.Length; // 💥 NullReferenceException
لا معنى لسؤال "لا شيء" عن طوله — هذا بالضبط ما يشتكي منه الخطأ.
قراءة رسالة الخطأ والـ Stack Trace
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
at ShopApp.OrderService.Checkout(Order order) in OrderService.cs:line 27
at ShopApp.Program.Main(String[] args) in Program.cs:line 9
- الرسالة نفسها لا تحدّد اسم المتغيّر — بعكس بعض اللغات الأخرى، فهي عامة دائمًا.
- أول سطر يخص كودك (
OrderService.cs:line 27) هو نقطة البداية — تجاهل أي أسطر من مكتبات .NET الداخلية أسفلها. - إن كان ذلك السطر يحتوي سلسلة استدعاءات طويلة، فكّكها مؤقتًا لأسطر منفصلة ليقودك رقم السطر إلى الحلقة الفارغة بالضبط.
الأسباب الأكثر شيوعًا
| السبب | مثال |
|---|---|
| متغيّر أُعلن ولم يُهيَّأ | List<string> items; ثم items.Add(...) |
| دالة أعادت null | Array.Find(people, p => p.Id == id) بلا تطابق |
| عنصر مصفوفة لم يُهيَّأ | new Person[5] — كل عنصر null حتى تُسنِد له قيمة |
| سلسلة استدعاءات طويلة | order.Customer.Address.City |
| حقل struct بقيمة افتراضية | Student s = default; يترك حقوله المرجعية null |
الحلول العملية
1) فعّل Nullable Reference Types
منذ C# 8، ميزة وقت ترجمة بحتة (لا تُبطئ التشغيل) تجعل المترجم يحذّرك عند أي احتمال لاستخدام null بلا فحص:
<!-- في ملف .csproj -->
<Nullable>enable</Nullable>
string required = "قيمة ثابتة"; // غير قابل لـ null — تحذير لو أسندت null له
string? optional = null; // صرّحت بأنه قد يكون null
مشاريع .NET الحديثة تفعّلها افتراضيًا. الفرق بين string وstring? يجعل
نيّتك واضحة للمترجم وللقارئ معًا.
2) استخدم العامل الشرطي الفارغ ?.
// ❌ ينفجر إذا كان customer فارغًا
string city = order.Customer.Address.City;
// ✅ يتوقف بأمان عند أول null في السلسلة
string? city = order.Customer?.Address?.City;
3) وفّر قيمة افتراضية بـ ??
string city = order.Customer?.Address?.City ?? "غير محدد";
4) هيّئ الحقول والمصفوفات من لحظة الإنشاء
// ❌ قنبلة موقوتة
private List<string> items;
// ✅ آمن منذ البداية
private List<string> items = new List<string>();
// ❌ عناصر المصفوفة تبقى null
Person[] people = new Person[5];
// ✅ عبّئها فورًا
Person[] people = { new Person("سارة"), new Person("خالد") };
5) افحص القيمة المُعادة من الدوال التي قد تُرجع null
Person? found = Array.Find(people, p => p.Id == searchId);
if (found is not null)
Console.WriteLine(found.Name);
else
Console.WriteLine("لم يُعثر على النتيجة");
قائمة تحقّق سريعة
- ما أول سطر في الـ stack trace يخص كودك أنت؟ ابدأ منه.
- هل فعّلت
<Nullable>enable</Nullable>بالمشروع؟ - هل الحقول والمصفوفات مهيّأة من لحظة الإعلان؟
- هل تفحص القيمة المُعادة من الدوال التي قد تكون null قبل استخدامها؟
- هل تستخدم
?.و??في سلاسل الوصول الطويلة؟
الخلاصة
NullReferenceException لا يعني أن كودك سيّئ، بل يقول بوضوح: "استدعيت شيئًا على
مرجع فارغ — في هذا السطر بالضبط." فعّل Nullable Reference Types ليحذّرك
المترجم مبكرًا، استخدم ?. و?? للوصول الآمن، وهيّئ حقولك ومصفوفاتك من
لحظة الإنشاء بدل تركها فارغة وانتظار الانفجار لاحقًا.
أكمل رحلتك في مسار C# بالعربي، وراجع
مرجع C# للعودة السريعة لعاملي ?. و??، وقارن مع
الفرق بين C++ و C# إن كنت قادمًا من خلفية أخرى.