Interview Handbook by Sayed Elmahdy English

المحور 06 · C# و .NET

C# 14 و .NET 10 والأداء

الجديد في C# 14 و .NET 10، ومقارنات القديم بالجديد، و Span، و source generators، و BenchmarkDotNet.

18 سؤالJunior · 5Mid · 7Senior · 610 اختيارات

المفاهيم

هنفهم إيه؟ هنتعلم إزاي ميزات C# الحديثة تساعد في كتابة كود أوضح، وإمتى تحسين الأداء يستاهل تعقيده. هنفصل بين ميزة compiler وميزة runtime، ونستخدم القياس قبل تغيير أو طريقة النشر، لأن syntax أحدث مش ضمان إن البرنامج أسرع.

قبل ما تبدأ: لازم تقرأ class وmethod وcollection بسيطة في C#. لو محتاج تأسيس، ابدأ بمحور 02. benchmark قياس متكرر لحالة محددة، وprofiling يبين أين يقضي التطبيق وقته؛ ما تعتمدش على التخمين وحده.

المصطلحات اللي تحتها خط منقّط تقدر تضغط عليها لمعرفة معناها، وترجع تكمل من نفس مكانك.

#خريطة C# 10 لحد 14 و .NET 6 لحد 10C# 10–14 and .NET 6–10 timeline

في الانترفيو، افصل language syntax عن runtime و libraries. C# 14 هو إصدار اللغة اللي مع 10 ؛ تغيير LangVersion لوحده مش بيضيف runtime ناقصة.

الإصداراللغةالمنصة
C# 10 / .NET 6global usings، و file-scoped namespaces، و record structsMinimal APIs، و DateOnly و TimeOnly، و Parallel.ForEachAsync، و WaitAsync
C# 11 / .NET 7required، و raw strings، و list patterns، و generic math supportRate limiting و output caching
C# 12 / .NET 8Primary constructors و collection expressionsKeyed ، و TimeProvider، و frozen collections، و IExceptionHandler، و dynamic افتراضي، و Minimal API Native
C# 13 / .NET 9params collections، و دعم Lock، و escape اسمه \e؛ field كان previewHybridCache، و OpenAPI generation، و Guid.CreateVersion7، و Task.WhenEach، و CountBy و AggregateBy و Index، و MapStaticAssets
C# 14 / .NET 10Extension members، و field، و null-conditional assignment، و nameof للـ unbound generics، و conversions، و modifiers، و partial constructors و events، و compound assignment operatorsFile-based apps؛ validation و OpenAPI 3.1 و و passkeys و System.Text.Json Patch؛ named filters و LeftJoin و RightJoin وتحسين complex types و JSON و vectors

.NET 10 نازل نوفمبر 2025 ودعمه لنوفمبر 2028. .NET 8 و .NET 9 دعمهم لنوفمبر 2026؛ من .NET 9 بقى 24 شهر. الأمثلة هنا تستهدف net10.0 و C# 14.

#Records و required و init و primary constructorsRecords, required, init and primary constructors

محتاج بيانات واضحة من غير boilerplate، وخدمة محتاجة dependency من غير constructor طويل. الأدوات دي بتقلل الكتابة، لكن مش بديل عن validation أو تصميم ownership.

public record Price(decimal Amount, string Currency);           // (1)
public sealed class CreateProduct
{
    public required string Name { get; init; }                  // (2)
    public required decimal Price { get; init; }
}
public sealed class CouponService(TimeProvider clock)           // (3)
{
    public bool IsValid(DateTimeOffset expiry) => clock.GetUtcNow() < expiry;
}

// Statements below belong in a caller method.
var price = new Price(100m, "EGP");
var taxed = price with { Amount = 114m };                        // (4)
List<int> original = [10, 20];
int[] ids = [.. original, 30];                                  // (5)
🧾 شرح الكود
  1. (1) Record class بيولّد value equality و init properties للـ positional parameters.
  2. (2) required يجبر source callers يعيّنوا property؛ init يقيّد التعيين لمرحلة الإنشاء. ممكن القيمة لسه تبقى invalid.
  3. (3) Primary constructor parameter متاح جوه class، لكنه مش property تلقائي ولا readonly.
  4. (4) with shallow copy؛ mutable references الداخلية ممكن تفضل مشتركة.
  5. (5) Collection expression محتاجة target type، والـ spread بينسخ العناصر، مش بينقل ownership.

اختار class عادية للـ entity اللي مساواتها بالهوية. required مش معناه non-empty، و init على List مش بيمنع Add. خليك صريح في runtime validation على حدود الـ .

#Pattern matching: اختبر النوع والشكل والقيمةPattern matching: type, shape and value

سلسلة if و casts بتخلّي business rules أصعب في القراءة. Patterns بتعبّر عن شروط على شكل الداتا، مع ترتيب واضح للحالات؛ أول arm مطابق هو اللي يكسب.

public record Parcel(decimal Total, string Country, decimal Weight);

static decimal Shipping(Parcel? order) => order switch
{
    null => throw new ArgumentNullException(nameof(order)),       // (1)
    { Total: >= 1000m } => 0m,                                  // (2)
    { Country: "EG", Weight: > 0m and <= 5m } => 50m,
    { Country: "SA" or "AE" } => 150m,
    _ => 300m                                                   // (3)
};

static string Describe(object? value) => value switch
{
    Price(> 0m, var currency) => currency,                       // (4)
    int[] [var first, .., var last] => $"{first}..{last}",        // (5)
    not null => "Other value",
    _ => "Missing"
};

static bool CanShip(string status, bool paid) =>
    (status, paid) is ("Ready", true);                           // (6)
🧾 شرح الكود
  1. (1) null له policy صريحة؛ property pattern مش بيطابق null.
  2. (2) Property و relational و logical patterns؛ and يربط حدود الوزن، و or بدائل الدولة.
  3. (3) Discard يغطي الباقي. تحذير exhaustiveness مفيد لكنه مش إثبات إن business rules كاملة.
  4. (4) Positional pattern بيستخدم Deconstruct بتاع record مع type test.
  5. (5) List pattern يطلب عنصرين على الأقل؛ .. يقبل أي طول في النص.
  6. (6) Tuple pattern بيجمع قرار معتمد على قيمتين.

لو القواعد طويلة ومتغيرة أو فيها ، استخدم methods أو strategy واضحة. ماتحطش side effects في getters المستخدمة في patterns، وخلي validation للوزن منفصل عن اختيار shipping price.

#C# 14: extension properties و static extension membersC# 14 extension properties and static extension members

Extension methods القديمة ما كانتش تسمح بـ property syntax. Extension blocks بتجمع members مرتبطة بنوع من غير تعديل النوع أو إضافة state ليه.

public static class ProductExtensions
{
    // Existing syntax still works.
    public static bool IsValidSku(this string sku) => sku.Length > 0;

    extension(string sku)                                  // (1)
    {
        public bool HasSkuText => !string.IsNullOrWhiteSpace(sku);
        public string NormalizeSku() => sku.Trim().ToUpperInvariant();
    }

    extension(string)                                      // (2)
    {
        public static string DefaultSku => "UNASSIGNED";
    }
}

// In a caller method with the extensions' namespace imported:
bool valid = " mug-01 ".HasSkuText;                         // (3)
string normalized = " mug-01 ".NormalizeSku();
string fallback = string.DefaultSku;                       // (4)
🧾 شرح الكود
  1. (1) Receiver اسمه sku للـ instance-like property والـ method.
  2. (2) Receiver type من غير اسم، علشان static extension property.
  3. (3) النداء بيبان زي property، لكن resolution وقت compile ومحتاج namespace في scope.
  4. (4) Static extension بتتنده على type نفسه.

مش وراثة ولا virtual dispatch، ومش وسيلة تضيف instance fields. Instance members الحقيقية ليها الأولوية. استخدم properties لحسابات واضحة ورخيصة، مش network call متخفي. مرجع extension members.

#C# 14: field و null-conditional assignment و nameofC# 14 field, null-conditional assignment and nameof

Property validation كانت محتاجة backing field يدوي، وتغيير property على object اختياري محتاج if. C# 14 بيختصر الاتنين، من غير ما يحوّلهم لضمانات concurrency أو validation كاملة.

public sealed class Product
{
    public string Sku
    {
        get;
        set => field = !string.IsNullOrWhiteSpace(value)    // (1)
            ? value.Trim().ToUpperInvariant()
            : throw new ArgumentException("SKU is required");
    } = "UNASSIGNED";
}

public sealed class Order { public string Status { get; set; } = "New"; }

// In a caller method:
Order? order = null;
order?.Status = CreateStatus();                            // (2)
string typeName = nameof(List<>);                          // (3) "List"

static string CreateStatus() => "Ready";
🧾 شرح الكود
  1. (1) field هو backing field بيولّده compiler. ده stable في C# 14 وكان preview في C# 13. Initializer مش بيمر على setter، فاختار initial value صالحة.
  2. (2) لو order null، مفيش assignment ولا تنفيذ CreateStatus. ++ و -- مش مسموحين بالشكل ده، وده مش lock.
  3. (3) nameof يقبل unbound generic من غير اختيار type argument؛ الناتج اسم مختصر مش unique type identifier.

القديم backing field و if صريح و nameof(List<int>) لسه صالح. لو عندك member اسمه field، استخدم this.field أو @field لتفادي الالتباس. متستخدمش null-conditional assignment لما غياب order لازم يكون error.

#C# 14: lambda modifiers و partial members و compound assignmentC# 14 lambda modifiers, partial members and compound assignment

بعض التحسينات بتخدم libraries والـ generators أكتر من application code. اعرف الاستخدام والحدود بدل تغيير كل الكود لمجرد وجود syntax جديد.

public delegate bool TryRead(string text, out int result);
// In a caller method:
// TryRead parse = (text, out result) => int.TryParse(text, out result); // (1)

public partial class OrderNotice
{
    public partial OrderNotice(string id);                  // (2) definition
    public partial event EventHandler? Sent;
}
public partial class OrderNotice
{
    private readonly string _id;
    private EventHandler? _sent;
    public partial OrderNotice(string id) { _id = id; }     // implementation
    public partial event EventHandler? Sent                // (3)
    {
        add => _sent += value;
        remove => _sent -= value;
    }
    public void Send() => _sent?.Invoke(this, EventArgs.Empty);
}

public sealed class InvoiceTotal
{
    public decimal Amount { get; private set; }
    public void operator +=(decimal amount)                 // (4)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(amount);
        Amount += amount;
    }
}
// Caller: var total = new InvoiceTotal(); total += 25m;
🧾 شرح الكود
  1. (1) الـ يحدد النوع، فـ تقدر تكتب out من غير explicit types. نفس الفكرة لـ ref و in و scoped و ref readonly؛ params لسه محتاج type صريح.
  2. (2) Partial constructor له defining declaration و implementing declaration بنفس التوقيع، غالباً في ملف generated.
  3. (3) Partial event لازم implementation فيه add و remove. ده فصل declaration عن التنفيذ، مش event جديد وقت التشغيل.
  4. (4) User-defined compound assignment هو public instance operator بيرجع void وياخد operand واحد، وبيعدّل نفس object.

القديم += كان يعتمد على operator + والتعيين. الجديد ممكن يغيّر object مشترك بين references؛ وثّق mutation وماتستخدموش في value object مفروض . Partial members مش محتاجة source generator لو أنت كاتب الجزئين. Partial members، و compound assignment.

#OLD vs NEW: اختيارات لها أسباب مش قواعد هجرة عمياءOld versus new: choices with reasons

استخدم الجدول لمناقشة الاختيار، مش للحكم إن الكود القديم غلط لمجرد سنّه. اعمل migration لما الاحتياج والـ compatibility والتكلفة يبرروه.

قديم أو تقليدياختيار حديثالسبب والحدود
Startup.csMinimal hosting في Program.csمن 6؛ نفس مبادئ و بكتابة أقل
Swashbuckle document + Microsoft.AspNetCore.OpenApi مع Scalar UIBuilt-in generation من .NET 9؛ UI منفصلة باختيارك؛ Swashbuckle لسه اختيار صالح
Newtonsoft.JsonSystem.Text.Json مع source generationMetadata وقت build؛ راجع converters واختلافات الـ contract قبل النقل
IMemoryCacheHybridCacheمن .NET 9؛ local cache، و optional distributed backend، و protection؛ البسيط لسه يناسبه IMemoryCache
Polly v7 policiesPolly v8 resilience pipelines و Microsoft.Extensions.Http.Resilienceتركيب strategies؛ راجع timeout و retry و
lock(object)lock(System.Threading.Lock)من C# 13 و .NET 9؛ synchronous بس، والقديم لسه صحيح
DateTime.NowTimeProvider.GetUtcNow()وقت قابل للاختبار من .NET 8؛ اعمل timezone conversion للعرض صراحةً
ArrayListList<T>Type safety، ومن غير للـ value elements
WebClientHttpClient و IHttpClientFactoryAsync و handler pooling؛ client طويل العمر مع PooledConnectionLifetime بديل صالح
MediatR / AutoMapperنسخ حالية بشروطها، أو explicit handlers و manual mapping و Mapperlyنقلوا لترخيص تجاري في 2025؛ راجع شروط الإصدار وأهلية الاستخدام، مش مجرد اسم package
FluentAssertions v8AwesomeAssertions أو Shouldly حسب الترخيص والاحتياجv8 تجاري؛ راجع قواعد فريقك. xUnit v3 هو الإصدار الحالي
// Web project; packages: Microsoft.AspNetCore.OpenApi, Scalar.AspNetCore.
using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();                          // (1)
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();                                  // (2)
    app.MapScalarApiReference();                        // (3)
}
app.MapGet("/products/{id:int}", (int id) => new { Id = id });
app.Run();
🧾 شرح الكود
  1. (1) يسجّل document generation؛ 10 بيستخدم OpenAPI 3.1 افتراضياً.
  2. (2) ينشر document، مش UI.
  3. (3) Scalar package تضيف UI منفصلة. المثال يفتحها في Development بس.

OpenAPI في ASP.NET Core و Scalar integration.

#Span و Memory و stackalloc و ArrayPoolSpan, Memory, stackalloc and ArrayPool

مثال خطوة بخطوة
  1. عندك مصفوفة bytes تمثل رسالة، ومحتاج تقرأ أول أربعة bytes فقط. إنشاء مصفوفة جديدة ونسخ الأربع قيم ممكن يضيف تخصيصًا غير لازم.
  2. يمثل نافذة على ذاكرة موجودة؛ تقطيع النافذة لا ينسخ البيانات. لو النافذة قابلة للكتابة وعدّلت عنصرًا فيها، يتغير نفس العنصر في الذاكرة الأصلية.
  3. ReadOnlySpan يمنع الكتابة من خلال النافذة دي، ولا يجمّد المصدر كله. راجع عمر الذاكرة وحدود استخدام Span؛ لو محتاج تحتفظ بالنافذة عبر await، ابحث عن Memory المناسب للحالة.

Span زي مسطرة على جزء من صفحة: بتبص على نفس الحروف بدل تصويرها. الـ view نفسه مش buffer جديد؛ لازم الذاكرة الأصلية تفضل صالحة طول استخدامه.

static int SumCsv(ReadOnlySpan<char> csv)
{
    int total = 0;
    foreach (Range range in csv.Split(','))                 // (1) .NET 9+
        total += int.Parse(csv[range]);
    return total;
}

static bool StartsWithValue<T>(ReadOnlySpan<T> values, T value)
    where T : IEquatable<T> =>
    !values.IsEmpty && values[0].Equals(value);

// In a caller method:
int[] ids = [10, 20];
bool starts = StartsWithValue(ids, 10);                      // (2) C# 14
Span<int> scratch = stackalloc int[32];                      // (3)
scratch.Clear();

static async Task CopyAsync(Stream source, Stream destination,
    CancellationToken ct)
{
    byte[] rented = ArrayPool<byte>.Shared.Rent(16 * 1024); // (4)
    try
    {
        Memory<byte> memory = rented.AsMemory(0, 16 * 1024);
        int read;
        while ((read = await source.ReadAsync(memory, ct)) != 0)
            await destination.WriteAsync(memory[..read], ct); // (5)
    }
    finally { ArrayPool<byte>.Shared.Return(rented, clearArray: true); }
}
🧾 شرح الكود
  1. (1) Split على span يطلع Ranges، و slicing مابيعملش strings جديدة. Parsing ممكن يرمي على input غلط؛ استخدم TryParse لو ده متوقع.
  2. (2) C# 14 يدعم span conversions في generic inference و overload resolution؛ مش اختراع Span نفسه، ومش كل conversion بقى متاح.
  3. (3) Stack buffer صغير ومحدود؛ لازم تهيئه قبل القراءة. ماتعملش stackalloc كبير أو جوه loop طويلة.
  4. (4) ArrayPool من System.Buffers؛ الحجم الفعلي ممكن أكبر والداتا قديمة.
  5. (5) Memory ينفع يعدّي await؛ اكتب الجزء المقروء بس، ورجّع array بعد آخر consumer. Clear له تكلفة لكنه يمنع تسليم بيانات buffer للمستخدم التالي.

Span ref struct؛ ماينفعش class field أو يبقى live عبر await. من C# 13 ممكن تستخدمه في أجزاء من async method من غير عبور suspension. Memory لا يملك العمر تلقائياً، وpool مش مناسب لو المستهلك محتفظ بالـ buffer بعد Return. C# 14 span conversions.

#قيس الأول: BenchmarkDotNet و profilingMeasure first: BenchmarkDotNet and profiling

Endpoint بطيء ممكن يكون مستني ، مش محتاج . ابدأ بـ تحدد المرحلة، و profiler يحدد hot path، وبعدها microbenchmark للمقارنة اللي فعلاً تهم.

// Console project; add the BenchmarkDotNet package.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

BenchmarkRunner.Run<CsvBenchmarks>();                      // (1)

[MemoryDiagnoser]
public class CsvBenchmarks
{
    private const string Csv = "12,7,30,44,5,9,100";

    [Benchmark(Baseline = true)]
    public int Split() => Csv.Split(',').Sum(int.Parse);    // (2)

    [Benchmark]
    public int Span()
    {
        int total = 0;
        ReadOnlySpan<char> input = Csv;
        foreach (Range range in input.Split(','))
            total += int.Parse(input[range]);              // (3)
        return total;
    }
}
dotnet run -c Release
🧾 شرح الكود
  1. (1) Runner يعمل القياسات مع warm-up؛ شغّله من غير debugger وبنفس البيئة المستهدفة.
  2. (2) Baseline بيفصل القيم لـ strings؛ بيرجع النتيجة علشان المقارنة تقيس شغل مستخدم.
  3. (3) البديل بيتجنب temporary strings؛ MemoryDiagnoser يبيّن . اتأكد إن الاتنين بيدّوا نفس النتيجة.

جرّب أحجام مختلفة و input غير صالح، واقرأ mean والتفاوت والـ allocated bytes. Benchmark صغير مش دليل على تحسن p99 تحت load. استخدم dotnet-counters للـ و thread pool، و dotnet-trace للـ ، و load test للتأكد من أثر التغيير. BenchmarkDotNet.

#Source generators و ValueTask: قلل تكلفة متقاسةSource generators and ValueTask: remove measured costs

Runtime مفيد، بس metadata discovery المتكرر ممكن يتنقل للـ build. Source generators بتضيف C# وقت compilation. ValueTask بيعالج تكلفة مختلفة: Task في عمليات غالباً بتخلص synchronously.

using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

public record ProductDto(int Id, string Name);

[JsonSerializable(typeof(ProductDto))]                    // (1)
public partial class ProductJson : JsonSerializerContext { }

public static partial class ProductPatterns
{
    [GeneratedRegex("^[A-Z]{3}-[0-9]{4}$")]
    public static partial Regex Sku();                   // (2)
}

// In a caller method:
// string json = JsonSerializer.Serialize(
//     new ProductDto(1, "Mug"), ProductJson.Default.ProductDto);

public sealed class StockReader
{
    public ValueTask<int> ReadAsync(int? cached, CancellationToken ct) =>
        cached is int count                              // (3)
            ? ValueTask.FromResult(count)
            : new ValueTask<int>(LoadAsync(ct));

    private static async Task<int> LoadAsync(CancellationToken ct)
    {
        await Task.Delay(10, ct); // stand-in for an asynchronous data source
        return 0;
    }
}
🧾 شرح الكود
  1. (1) context يولّد metadata للنوع المصرّح بيه؛ مش لكل type في البرنامج.
  2. (2) GeneratedRegex يولّد implementation وقت build بدل discovery المعتاد وقت التشغيل.
  3. (3) Cache hit يرجع قيمة من غير Task جديد. Miss لسه ممكن يعمل allocation؛ ده مثال للتكلفة مش cache thread-safe كامل.

Default للـ async هو Task. استهلك ValueTask مرة واحدة؛ لو محتاج await متكرر أو composition حوّله لـ Task مرة واحتفظ بيه. Generator مش ضمان إن كل dependency -safe. أمثلة تانية: LoggerMessage و Mapperly؛ راجع generated code والـ diagnostics بدل اعتباره سحر.

#Native AOT: مكاسب وحدود النشرNative AOT deployment trade-offs

لو أو خدمة قصيرة العمر بتدفع تكلفة startup كتير، Native ينقل compile لوقت publish. لكن لو bottleneck في ، ده مش علاج تلقائي.

dotnet publish -c Release -r linux-x64 -p:PublishAot=true

الأمر بيستهدف و architecture محددين، وبيحتاج native toolchain مناسب. النتيجة مفيهاش ، لكن فيها وخدمات runtime مرتبطة. اختبر الـ published artifact نفسه، وراجع trimming و AOT warnings من غير إسكاتها عشوائياً.

مكسب محتملالتكلفة
Cold start و memory أقلBuild أطول؛ القياس الفعلي لازم
Executable مستقل عن shared runtime مثبتإعادة publish لتحديث خدمات runtime المضمّنة
معرفة الكود والـ metadata بدريمفيش .Emit؛ reflection اللي metadata بتاعته محفوظة ممكن يشتغل
Minimal تدعم المسار ده controllers مش اختيار Native AOT مدعوم؛ افحص مكتباتك والـ providers

يقلل startup مع الاحتفاظ بـ JIT؛ JIT يقدر يستفيد من dynamic أثناء التشغيل. التفاصيل في موضوع 03: runtime. متفترضش إن AOT أصغر أو أسرع في كل workload.

#File-based apps و Aspire: حاجتين مختلفتينFile-based apps and Aspire: different jobs

Script صغير لتحويل invoices مش محتاج solution كاملة. أما مع Redis و database محلياً فمحتاج تنسيق dependencies. File-based apps بتبسّط الأول؛ Aspire بينظّم التاني.

// app.cs; .NET 10 SDK. Package restore requires network access or a cache.
#:package Humanizer@2.14.1                                  // (1)
using Humanizer;
Console.WriteLine(TimeSpan.FromMinutes(90).Humanize());
dotnet run app.cs
# Use --file explicitly if the directory also contains a project.
dotnet run --file app.cs
// AppHost project with Aspire.Hosting.Redis and a reference to Store.Api.
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");                      // (2)
builder.AddProject<Projects.Store_Api>("api")
    .WithReference(cache)                                  // (3)
    .WaitFor(cache);
builder.Build().Run();
🧾 شرح الكود
  1. (1) #:package يصرّح بـ NuGet dependency بنسخة محددة من غير csproj مكتوب بإيدك. مفيش علاقة بين السطر ده و AppHost اللي بعده.
  2. (2) Aspire AppHost يعرّف Redis resource؛ container runtime مطلوب للـ container resource.
  3. (3) Reference يمرر connection information، و WaitFor ينتظر readiness. Projects.Store_Api اسم generated من project reference؛ التطبيق لسه محتاج يسجّل Redis client مناسب.

ServiceDefaults لما تضيفها للخدمات بتجهز telemetry و و resilience defaults، و dashboard بيجمع logs و و metrics. Aspire، اللي كان اسمه Aspire، مفيد في local orchestration؛ نشر production محتاج اختيار target وتهيئة منفصلة. لو tool كبرت لموديولات واختبارات، انقلها لمشروع عادي. File-based apps و Aspire overview.

أسئلة الانترفيو

راجع فهمك

ليه الاختيار ده مناسب؟ إمتى ممكن ماينفعش؟ وإيه اللي هيتغير لو الحمل زاد أو ظهر فشل؟

6.1What do records provide, and are they deeply immutable?Junior

Records بتولّد equality و with؛ positional record class فيها init properties، لكن mutable fields أو collections ممكن تغيّر state، و with shallow copy.

الإجابة النموذجية (English)

I use records when value-based equality fits the data, such as a price or response . They generate equality, useful formatting and support for with expressions. A positional record class has init-only properties, but records are not automatically deeply . A List inside a record can still change, and with makes a shallow copy that can share that List. I choose immutable contained values when the contract requires a stable value object.

6.2Do required and init replace runtime input validation?Junior

required يلزم تعيين property في source، و init يحدد توقيت التعيين. لا واحد يمنع empty name أو negative price لوحده.

الإجابة النموذجية (English)

No. required makes ordinary source callers initialize a member, while init restricts when a property can be assigned. Neither establishes business validity or deep . A required string can still be empty, and an init-only List can still be modified. I use these features to improve construction contracts, then validate external requests at runtime and enforce domain where state changes occur. Compiler assistance and runtime validation solve different parts of the problem.

6.3What does a primary constructor generate for a normal class?Junior

Parameters متاحة جوه النوع، لكن مش properties تلقائي ولا readonly. ده مختلف عن positional record اللي بيولّد properties.

الإجابة النموذجية (English)

A primary constructor lets me declare constructor parameters on the class declaration and use them within the type. For a normal class, those parameters do not automatically become public properties, unlike positional record parameters. They are not readonly either. I use the syntax for concise dependency injection when it improves clarity. If I need an explicit readonly field or constructor validation with a clearer lifetime contract, a conventional constructor remains a good choice.

6.4What is pattern matching useful for?Junior

بيجمع type checking واستخراج properties وشروط القيم. ترتيب arms مهم، والـ discard يغطي الباقي مش بيثبت صحة business rules.

الإجابة النموذجية (English)

I use pattern matching to express decisions based on a value's type, shape and properties without repeated casts or deeply nested conditionals. Property and relational patterns work well for shipping rules, logical patterns combine alternatives, and list patterns describe sequence shapes. Switch expressions evaluate arms in order. I keep rules readable and handle null explicitly, but I still test business boundaries because a syntactically exhaustive switch can contain incorrect or overly broad rules.

6.5What do collection expressions and spread do?Junior

نفس syntax لـ arrays و lists مع target type واضح. Spread يضيف عناصر المصدر للنتيجة، لكنه مش deep copy.

الإجابة النموذجية (English)

Collection expressions give a compact syntax for constructing supported collection types, such as an int array or a List. A spread element includes the elements of another sequence. The expression needs an appropriate target type, so I do not assume an untyped var declaration will infer a List. It creates a collection structure, not a deep clone of referenced objects, and enumerating a spread source can have a cost if that source is deferred.

6.6What are the important C# 14 changes?Mid

اذكر كل feature بمعناها: extensions و field و ?. assignment و nameof و inference و modifiers و partial members و compound operators، مع مثال مفيد بدل قائمة محفوظة.

الإجابة النموذجية (English)

I group them by purpose. Extension blocks add properties and static extension members. The field keyword reduces property boilerplate, null-conditional assignment simplifies optional updates, and nameof accepts unbound generics. conversions integrate better with inference and overload resolution. can use parameter modifiers without explicit types, while partial constructors and events help generated code. User-defined compound assignment can mutate a type in place. I adopt each feature when its semantics make the code clearer, rather than converting everything.

6.7How do extension properties differ from adding real instance state?Mid

Extension property حساب خارجي بيظهر كسلوك للنوع؛ مفيش field بيتضاف، والـ binding وقت compile، والـ instance member الحقيقي أولى.

الإجابة النموذجية (English)

An extension property is declared outside the extended type and resolved at compile time when its namespace is in scope. It does not add storage, change the type layout or participate in virtual dispatch. I use it for small derived values that naturally read as properties, such as checking whether a SKU has text. Real instance members take precedence, and I avoid hiding expensive behind property syntax because callers expect simple access.

6.8What does order?.Status = CreateStatus() do if order is null?Mid

لا assignment ولا تنفيذ يمين =. دي convenience مش synchronization، ومش مناسبة لو عدم وجود order لازم يتسجّل كفشل.

الإجابة النموذجية (English)

It skips both the assignment and evaluation of CreateStatus. That short-circuit behavior is useful when absence is an accepted case. I would use an explicit guard instead if a missing order violates the operation's contract, because silently skipping work could hide a bug. The syntax provides no thread synchronization or semantics. I also distinguish it from increment and decrement, which are not supported through null-conditional assignment in this form.

6.9Why would you use TimeProvider rather than DateTime.Now?Mid

TimeProvider dependency قابلة للتبديل في الاختبارات. GetUtcNow مناسب للتخزين والمقارنة؛ تحويل local time للعرض قرار منفصل.

الإجابة النموذجية (English)

I inject TimeProvider when business behavior depends on time, such as coupon expiry. Production uses the system provider, while tests can control the clock without sleeping or changing the machine time. GetUtcNow gives an explicit instant suitable for comparisons; I convert to the user's time zone for display. This improves determinism rather than making clock reads inherently faster. I also separate elapsed-time measurement from wall-clock time when timing an operation.

6.10What changed with OpenAPI generation, and where does Scalar fit?Mid

Built-in generation من 9، و OpenAPI 3.1 افتراضي في 10. Scalar package مستقلة؛ Swashbuckle لسه ممكن تستخدمه.

الإجابة النموذجية (English)

provides built-in OpenAPI document generation from 9 through Microsoft.AspNetCore.OpenApi, and ASP.NET Core 10 defaults to OpenAPI 3.1. That document is separate from an interactive . Scalar is one UI integration I can add as a separate package. Swashbuckle remains a valid choice even though it left the default templates. I check transformers, compatibility and access policy before migrating an existing documentation setup.

6.11How would you approach optimizing a slow endpoint?Mid

قيس stages بالـ ، وابدأ بالمسبب الفعلي. و network و contention قبل micro-optimizations لو هم اللي واخدين الوقت، وبعدين قيس تاني.

الإجابة النموذجية (English)

I define a measurable target and reproduce the issue under representative load. show whether time goes into , external calls, queueing or application . I inspect the relevant query plans or profile the hot code, then make one justified change. BenchmarkDotNet helps compare small implementations, but I verify the application result with , tail and measurements. I would not rewrite code with or switch to before identifying the bottleneck.

6.12How should licensing changes affect library selection?Mid

افصل الفني عن شروط الاستخدام؛ MediatR و AutoMapper غيّروا الترخيص في 2025، و FluentAssertions v8 تجاري. شروط النسخة والأهلية أهم من حكم عام.

الإجابة النموذجية (English)

I review the terms of the exact version and the organization's eligibility, rather than assuming every version has identical conditions. MediatR and AutoMapper changed their commercial licensing model in 2025, and FluentAssertions v8 is commercial. Depending on requirements, explicit handlers, manual mapping or Mapperly may be alternatives, while AwesomeAssertions and Shouldly are assertion options. I include migration and maintenance costs in the decision and avoid presenting a licensing change as a technical defect.

6.13How do Span and Memory differ, especially with async code?Senior

view محدود بالعمر ومش class field ولا يعدّي await. Memory ممكن يتخزن ويعدّي await، لكن مش معناه إنه مالك الذاكرة أو يمنع Return مبكر.

الإجابة النموذجية (English)

is a ref-struct view over contiguous memory, useful for -conscious synchronous processing. It cannot be a class field or remain live across an await. Modern C# allows using it in async method regions that do not cross suspension. Memory is the storable counterpart that can cross awaits, from which I obtain a span while processing. Neither abstraction automatically owns the underlying buffer, so I still enforce lifetime, especially when arrays come from a pool.

6.14What can go wrong with ArrayPool or stackalloc?Senior

Pool بيرجع array أكبر وdirty؛ احسب length ورجّعها بعد آخر استخدام. Stackalloc كبير أو متكرر ممكن يستهلك stack؛ مفيش ضمان initial contents.

الإجابة النموذجية (English)

A pooled array may be larger than requested and contain previous data, so I track the valid length and never expose unused bytes. I return it exactly once in finally after asynchronous consumers finish, and consider clearing sensitive contents. With stackalloc I keep sizes small and bounded, initialize before reading and avoid repeated inside long loops. Both tools trade simpler allocation for stronger lifetime responsibilities; I use them only after measuring a worthwhile benefit.

6.15When is ValueTask worth its extra constraints?Senior

لما measured hot path غالباً synchronous، زي cache hit. Consume مرة واحدة، و Task الافتراضي لو مفيش دليل إن الفرق مهم.

الإجابة النموذجية (English)

I consider ValueTask for a measured hot path that completes synchronously often enough for Task to matter. It can hold a direct result, a Task or a reusable source, which is why consumers must follow single-consumption rules rather than assuming repeated awaits are safe. It also makes composition less convenient and has its own struct-copy costs. I default to Task; when repeated awaits are needed, I convert once to a Task and reuse that.

6.16Do source generators eliminate all reflection and AOT problems?Senior

لأ؛ generator يولّد كود لحالات محددة، والـ dependency chain لسه محتاجة مراجعة. context لازم يغطي الأنواع المستخدمة فعلاً.

الإجابة النموذجية (English)

No. A source generator moves specific work into compilation, such as metadata generation, logging helpers or regular-expression implementation. It does not guarantee that every dependency avoids runtime code generation or preserves all metadata needed after trimming. I inspect generated output, build diagnostics and the published artifact's behavior. For JSON I ensure the context covers the actual serialized types and converters, then test the binary rather than treating a successful ordinary build as sufficient.

6.17What does Native AOT trade for startup improvements?Senior

Build أطول وتحديد platform وقيود dynamic code و metadata. لسه موجود، و warmed-up ممكن أحسن حسب workload.

الإجابة النموذجية (English)

Native moves compilation to publishing and removes the runtime , which can improve cold starts and memory usage. It requires platform-specific builds and a native toolchain, limits runtime code generation and needs deliberate metadata preservation. and other runtime services still exist in the executable. I audit dependencies and measure the target workload, because a long-running JIT service can benefit from dynamic and may not be limited by startup at all.

6.18What do file-based apps and Aspire each add to a workflow?Senior

File-based app لأداة صغيرة من cs واحد و package directives؛ Aspire AppHost لتنسيق resources وخدمات محلياً مع defaults و telemetry.

الإجابة النموذجية (English)

A file-based app lets me run a small C# tool directly with the 10 and declare packages through directives, without maintaining a project file initially. Aspire addresses a different scale: its AppHost describes services and dependencies, wires connection information and coordinates local startup. ServiceDefaults and the dashboard support observability and health conventions. I still configure clients and decide how production is deployed; neither feature removes the need for application design or testing.

اختبر نفسك

إجابات الاختبار وعلامات إكمال المحاور بتتحفظ على المتصفح ده، ومش بتتنقل لأجهزة تانية.

النتيجة: 0 / 0

Which statement about required is correct?

التعيين غير صلاحية القيمة؛ runtime validation لسه مهم.

What happens to the right-hand expression in a null-conditional assignment on null?

الـ assignment نفسها skipped.

What does nameof(List<>) produce in C# 14?

nameof بيرجع اسم مختصر وقت compile.

What is the signature shape of a C# 14 user-defined += operator?

بيعدّل instance الحالية، ومش محتاج يرجّع object جديد.

Which feature makes array-to-span generic inference more natural in C# 14?

كان موجود من زمان؛ الجديد اندماجه مع قواعد اللغة.

What must a partial event's implementing declaration contain?

Defining declaration و implementing declaration لهم أدوار مختلفة.

When should a rented array be returned?

Return مبكر يخلي مستخدم تاني يعدّل نفس buffer.

What does AddOpenApi provide without an extra UI package?

Document generation والـ interactive حاجتين منفصلتين.

What is the first useful step for an endpoint with high p99 latency?

اختيار optimization لازم يتبع دليل.

Which is a core Aspire AppHost responsibility?

AppHost ينظم resources؛ production target قرار منفصل.