الوظيفة:
مطور دوت نت
المستوى:
حديث التخرج
عدد الأسئلة:
١٢ سؤالاً
الزمن:
٦ دقائق

أسئلة انترفيو دوت نت مع الإجابة لحديثي التخرج

أسئلة تُطرح فعلاً في مقابلات وظيفة مطور دوت نت لحديثي التخرج: أساسيات C#، والبرمجة الكائنية، وASP.NET Core، وEntity Framework وLINQ، وSQL Server، والبرمجة غير المتزامنة. في آخرها تقرير بنقاط ضعفك.

المهارات: أساسيات لغة C#، البرمجة كائنية التوجه وأنماط التصميم، تطبيقات ASP.NET Core وWeb API، البرمجة غير المتزامنة والأداء، إطار Entity Framework وLINQ، قواعد بيانات SQL Server

تعليمات: لكل سؤال إجابة صحيحة واحدة. إن لم تعرف الإجابة فاختر «لا أعرف»، وسيدلّك التقرير على ما تراجعه.

السؤال الأول: إذا كان Point معرّفاً على النحو struct Point { public int X; }، فماذا يطبع الكود التالي؟ var a = new Point { X = 1 }; var b = a; b.X = 5; Console.WriteLine(a.X);

اختر إجابتك لتبدأ الاختبار

كل الأسئلة مع الإجابات (١٢ سؤالاً)

جرّب المقابلة أولاً، ثم افتح الإجابات لتراجعها مع الشرح.
  1. ١. إذا كان Point معرّفاً على النحو struct Point { public int X; }، فماذا يطبع الكود التالي؟ var a = new Point { X = 1 }; var b = a; b.X = 5; Console.WriteLine(a.X);

    • 0
    • 5
    • يحدث خطأ ترجمة compile error
    • 1

    الشرح: struct نوع قيمة value type، لذلك ينسخ الإسناد b = a البيانات نفسها، ويبقى a.X مساوياً لـ 1. يجيب كثيرون 5 لأنهم يعاملون struct كأنه class يُنسخ فيه المرجع فقط.

  2. ٢. ما الفرق بين const وreadonly عند تعريف حقل داخل صنف في C#؟

    • لا فرق بينهما في السلوك، والفرق الوحيد أن const أسرع لأن المترجم يُحسّن الوصول إليه
    • قيمة readonly تُحدد وقت الترجمة، وconst يمكن إسناده عند التعريف أو في المُنشئ
    • يمكن تعديل readonly من أي دالة في الصنف، أما const فلا يُعدل بعد تعريفه
    • قيمة const تُحدد وقت الترجمة، وreadonly يمكن إسناده عند التعريف أو في المُنشئ

    الشرح: const ثابت وقت الترجمة تُعطى قيمته عند التعريف فقط، أما readonly فثابت وقت التشغيل يمكن إسناده عند التعريف أو داخل المُنشئ ثم لا يتغير. يخلط بعض المرشحين بينهما أو يظنون أن readonly قابل للتعديل من أي دالة.

  3. ٣. بافتراض تعريف الأصناف التالية: class Animal { public virtual string Speak() => "Animal"; } class Dog : Animal { public override string Speak() => "Dog"; } class Cat : Animal { public new string Speak() => "Cat"; } ماذا يطبع الكود التالي؟ Animal d = new Dog(); Animal c = new Cat(); Console.WriteLine($"{d.Speak()} {c.Speak()}");

    • Animal Animal
    • Animal Cat
    • Dog Cat
    • Dog Animal

    الشرح: override يستبدل تنفيذ الدالة الافتراضية فيُستدعى تنفيذ Dog حتى عبر مرجع من نوع Animal، أما new فيُخفي الدالة فقط فيُستدعى تنفيذ Animal لأن نوع المرجع هو Animal. يظن كثيرون أن new يعمل مثل override.

  4. ٤. أي العبارات التالية صحيحة عن الفرق بين الصنف المجرد abstract class والواجهة interface في الإصدارات الحديثة من C#؟

    • يمكن للصنف أن يرث من عدة أصناف مجردة كما يطبق عدة واجهات
    • الصنف المجرد لا يمكن أن يحتوي على دوال مُنفذة، بل تعريفات فقط
    • الصنف المجرد يقبل instance fields، أما الواجهة فلا
    • الواجهة لا يمكن أن تحتوي على أي تنفيذ للدوال، بعكس الصنف المجرد

    الشرح: لا تسمح الواجهة بحقول تحفظ حالة الكائن، بينما يستطيع الصنف المجرد ذلك إضافة إلى المُنشئات، والصنف يرث من صنف أساسي واحد فقط. يكرر كثيرون أن الواجهة لا تحتوي على أي تنفيذ، لكن C# 8 أضاف التنفيذ الافتراضي default interface methods.

  5. ٥. سجّلت خدمة في تطبيق ASP.NET Core باستخدام builder.Services.AddScoped<IOrderService, OrderService>(). كم نسخة من OrderService يُنشئها حاوي حقن التبعيات؟

    • نسخة جديدة في كل مرة تُطلب فيها الخدمة، حتى داخل الطلب نفسه
    • نسخة واحدة لكل مستخدم تبقى طوال جلسته session
    • نسخة واحدة فقط طوال عمر التطبيق تُشارك بين كل الطلبات
    • نسخة واحدة لكل طلب HTTP تُشارك داخل هذا الطلب

    الشرح: AddScoped ينشئ نسخة واحدة لكل نطاق scope، وASP.NET Core ينشئ نطاقاً لكل طلب، فتتشارك المكونات داخل الطلب الواحد النسخة نفسها. يخلط المرشحون عادة بينه وبين AddTransient الذي ينشئ نسخة عند كل طلب للخدمة، وAddSingleton الذي ينشئ نسخة واحدة للتطبيق كله.

  6. ٦. في وحدة تحكم controller تحمل السمة [ApiController]، أرسل العميل طلباً يغيب عن جسمه الحقل Name المعلَّم بـ [Required] في النموذج. ماذا يحدث افتراضياً؟

    • يُنفَّذ الإجراء، وعليك فحص ModelState.IsValid بنفسك لإرجاع الخطأ
    • تُضبط قيمة Name على null ويُكمل الإجراء تنفيذه بشكل طبيعي
    • يُعيد الإطار 400 Bad Request تلقائياً دون تنفيذ الإجراء
    • يُعيد الإطار 500 Internal Server Error لأن التحقق فشل

    الشرح: السمة [ApiController] تجعل أخطاء التحقق من النموذج تُعيد استجابة 400 تلقائياً مع تفاصيل الأخطاء، فلا حاجة لفحص ModelState.IsValid يدوياً. يجيب كثيرون بالفحص اليدوي لأنه السلوك المعتاد في وحدات التحكم التي لا تحمل هذه السمة.

  7. ٧. في إجراء action داخل تطبيق ASP.NET Core، لماذا يُفضَّل await _context.Orders.ToListAsync() على _context.Orders.ToListAsync().Result؟

    • لأن await يحرر الخيط ليخدم طلبات أخرى أثناء الانتظار
    • لأن await يجعل الاستعلام يُنفذ أسرع داخل قاعدة البيانات نفسها
    • لأن .Result تُرجع كائن Task غير مكتمل بدلاً من قائمة الطلبات
    • لأن await ينفذ الاستعلام على خيط جديد بالتوازي مع بقية الطلب

    الشرح: أثناء انتظار عملية I/O يعيد await الخيط إلى thread pool ليخدم طلبات أخرى، بينما يحجز .Result الخيط حتى تنتهي العملية، ومع كثرة الطلبات يؤدي ذلك إلى استنزاف الخيوط thread pool starvation. يظن كثيرون خطأً أن async يسرّع الاستعلام نفسه أو يشغّله على خيط جديد.

  8. ٨. لماذا يُنصح بتجنب الدوال async void في C# إلا في معالجات الأحداث event handlers؟

    • لأن المستدعي لا يستطيع انتظارها أو التقاط استثناءاتها
    • لأنها أبطأ في التنفيذ من async Task بسبب تكلفة إضافية
    • لأنها تعمل دائماً بشكل متزامن ولا تحرر الخيط أثناء الانتظار
    • لأن المترجم يرفضها في أي مكان خارج معالجات الأحداث

    الشرح: لا تُرجع async void كائن Task، فلا يعرف المستدعي متى تنتهي، وأي استثناء فيها لا يصل إلى try/catch المحيط بالاستدعاء وقد يُسقط التطبيق. يظن بعضهم أن المشكلة في الأداء، بينما المشكلة في التحكم بالانتهاء والأخطاء.

  9. ٩. ماذا يطبع الكود التالي؟ var numbers = new List<int> { 1, 2, 3 }; var query = numbers.Where(n => n > 1); numbers.Add(4); Console.WriteLine(query.Count());

    • يُرمى InvalidOperationException لأن القائمة تغيرت
    • 3
    • 2
    • 4

    الشرح: استعلامات LINQ مؤجلة التنفيذ deferred execution، فلا يُنفذ Where إلا عند استدعاء Count()، وعندها تحتوي القائمة على 1, 2, 3, 4 فيُعاد 3. يجيب كثيرون 2 لظنهم أن الاستعلام نُفذ لحظة تعريفه.

  10. ١٠. ما المشكلة في هذا الاستعلام باستخدام EF Core؟ var expensive = _context.Products .ToList() .Where(p => p.Price > 100) .ToList();

    • لا مشكلة، فـ EF Core يحوّل Where إلى SQL في كل الأحوال
    • لن يُترجم الكود لأن Where لا يمكن استدعاؤها بعد ToList()
    • يُرسل استعلاماً منفصلاً إلى قاعدة البيانات لكل منتج في الجدول
    • يجلب كل المنتجات من قاعدة البيانات ثم يُرشحها في الذاكرة

    الشرح: أول ToList() يُنفذ الاستعلام فوراً ويجلب الجدول كله، ثم يعمل Where على IEnumerable في الذاكرة بدلاً من أن يُترجم إلى شرط WHERE في SQL. الصواب وضع Where قبل ToList() ليبقى الاستعلام IQueryable ويُرشَّح داخل قاعدة البيانات.

  11. ١١. المتغير name قادم من مدخلات المستخدم. أي استدعاء في EF Core يحمي من هجمات SQL injection؟

    • context.Users.FromSql($"SELECT * FROM Users WHERE Name = {name}")
    • context.Users.FromSqlRaw(string.Format("SELECT * FROM Users WHERE Name = '{0}'", name))
    • context.Users.FromSqlRaw("SELECT * FROM Users WHERE Name = '" + name + "'")
    • context.Users.FromSqlRaw($"SELECT * FROM Users WHERE Name = '{name}'")

    الشرح: FromSql يستقبل FormattableString فيحوّل كل قيمة مُدرجة إلى DbParameter يُرسل منفصلاً عن نص الاستعلام. أما FromSqlRaw مع نص مبني بالدمج أو الإدراج فيضع قيمة المستخدم داخل نص SQL نفسه، وهذا ما يغفل عنه من يظن أن علامة $ وحدها تكفي للحماية.

  12. ١٢. كم فهرساً من نوع clustered index يمكن إنشاؤه على جدول واحد في SQL Server؟

    • حتى 999 فهرساً
    • فهرس واحد لكل عمود في الجدول
    • فهرس واحد فقط
    • عدد غير محدود

    الشرح: يحدد clustered index الترتيب الذي تُخزن به صفوف الجدول نفسها، ولا يمكن تخزين الصفوف إلا بترتيب واحد. أما الرقم 999 فهو الحد الأقصى لفهارس nonclustered index، وكثيراً ما يخلط المرشحون بين الرقمين.

المزيد