مقدمة
\nمقابلة مطور C# ليست مجرد اختبار للصياغة النحوية. يرغب أصحاب العمل في رؤية فهم منصة .NET، ومبادئ عمل CLR، والقدرة على كتابة كود فعال، ومعرفة الأساليب الحديثة (async, LINQ, DI).
\nفي هذه المقالة، سنستعرض أهم أسئلة C# من الألف إلى الياء - من أنواع البيانات الأساسية إلى الموضوعات المتقدمة مثل جمع القمامة والبرمجة غير المتزامنة. كل سؤال مصحوب بإجابة مختصرة ومثال كود، لتكون واثقًا في المقابلة.
\n\n1. الأسئلة الأساسية: الأنواع، الفئات والذاكرة
\n\n1.1. ما الفرق بين Value Type و Reference Type؟
\nValue Type (int, struct, enum) تُخزن في المكدس وتُنسخ بالقيمة. Reference Type (class, string, array) تُخزن في الكومة، والمتغير يحتوي على مرجع للكائن.
\nint a = 10;\nint b = a; // b نسخة، a و b مستقلان\n\nclass MyClass { public int X; }\nMyClass obj1 = new MyClass { X = 5 };\nMyClass obj2 = obj1; // obj2 يشير إلى نفس الكائن\nobj2.X = 10; // obj1.X سيصبح أيضًا 10\n\n1.2. ما هو Boxing و Unboxing؟
\nBoxing هو تغليف النوع القيمي في كائن (في الكومة). Unboxing هو العملية العكسية. الاستخدام المتكرر يقلل الأداء.
\nint number = 42;\nobject boxed = number; // Boxing\nint unboxed = (int)boxed; // Unboxing\n\n1.3. حدثنا عن جامع القمامة (Garbage Collector).
\nGC في .NET يحرر الذاكرة تلقائيًا من الكائنات التي لا توجد مراجع لها. يعمل بأجيال (Gen 0, 1, 2). الكائنات كبيرة الحجم (>85 كيلوبايت) تدخل في Large Object Heap (LOH).
\nنصيحة: لا تستدعِ GC.Collect() يدويًا - فهذا يعطل تحسينات GC.
2. البرمجة كائنية التوجه ومبادئ التصميم
\n\n2.1. اشرح مبادئ SOLID.
\n- \n
- S — Single Responsibility: يجب أن يكون للفصل سبب واحد للتغيير. \n
- O — Open/Closed: مفتوح للتوسع، مغلق للتعديل. \n
- L — Liskov Substitution: يجب أن تحل الفئات الفرعية محل الفئة الأساسية بشكل صحيح. \n
- I — Interface Segregation: واجهات صغيرة متعددة أفضل من واجهة واحدة كبيرة. \n
- D — Dependency Inversion: يجب أن تعتمد التبعيات على التجريدات وليس على الفئات الملموسة. \n
2.2. ما هو Dependency Injection (DI)؟
\nDI هو نمط يتم فيه تمرير التبعيات إلى الفصل من الخارج (عبر المُنشئ أو الطريقة أو الخاصية). في .NET Core يوجد حاوية DI مدمجة.
\npublic interface ILogger\n{\n void Log(string message);\n}\n\npublic class ConsoleLogger : ILogger\n{\n public void Log(string message) => Console.WriteLine(message);\n}\n\npublic class OrderService\n{\n private readonly ILogger _logger;\n public OrderService(ILogger logger) // DI عبر المُنشئ\n {\n _logger = logger;\n }\n}\n\n3. LINQ، الاستثناءات والسلاسل النصية
\n\n3.1. كيف يعمل LINQ؟ ما هو التنفيذ المؤجل؟
\nLINQ (Language Integrated Query) يسمح بكتابة استعلامات للمجموعات، قواعد البيانات (EF Core) و XML. التنفيذ المؤجل يعني أن الاستعلام يُنفذ فقط عند تكرار النتيجة (مثلًا عبر foreach أو .ToList()).
var numbers = new List<int> { 1, 2, 3, 4, 5 };\nvar query = numbers.Where(n => n > 2); // لم يتم تنفيذ الاستعلام\nnumbers.Add(6); // إضافة بعد إنشاء الاستعلام\nforeach (var n in query) // التنفيذ هنا\n Console.WriteLine(n); // 3, 4, 5, 6\n\n3.2. ما الفرق بين String و StringBuilder؟
\nString - نوع غير قابل للتغيير. كل عملية دمج تنشئ كائنًا جديدًا. StringBuilder يستخدم مخزنًا مؤقتًا قابلًا للتغيير وهو فعال للتعديلات المتعددة على السلسلة النصية.
\n// سيء (تخصيصات كثيرة)\nstring s = "";\nfor (int i = 0; i < 1000; i++)\n s += i.ToString(); // يتم إنشاء 1000 كائن!\n\n// جيد\nStringBuilder sb = new StringBuilder();\nfor (int i = 0; i < 1000; i++)\n sb.Append(i.ToString());\nstring result = sb.ToString();