المفاهيم
هنفهم إيه؟ هنستخدم أساسيات C# عشان نمثل قواعد زي دفع طلب أو التحقق من بياناته، ونختار النوع المناسب للقيم والسلوك. هتشوف إزاي التصميم يمنع حالات غلط، وإمتى قيمة بتتنقل بنسخة أو بمشاركة نفس الكائن.
قبل ما تبدأ: المتغير اسم لقيمة نستخدمها، والـ function مجموعة خطوات لها مدخلات ونتيجة، والـ class وصف لنوع كائن يجمع بيانات وسلوك. مش لازم تعرف قبل كده؛ ابدأ بالمثال الأول، واعتبر كلمة object نسخة معمولة من class.
المصطلحات اللي تحتها خط منقّط تقدر تضغط عليها لمعرفة معناها، وترجع تكمل من نفس مكانك.
#الـ 4 Pillars بتوع OOPThe four pillars of OOP
تخيّل checkout بيقبل كارت أو محفظة: الـ caller محتاج يعرف طريقة الدفع المتاحة، لكن مش محتاج يعرف تفاصيل خصم الفلوس. المثال بيستخدم ده عشان يربط الـ four pillars بمسؤولية واضحة؛ هو نموذج تعليمي صغير، مش معالجة دفع حقيقية.
خد العربية كمثال:
- Encapsulation (التغليف): الموتور مقفول جوه الكبوت. إنت بتتعامل مع الدواسة بس، ومش بتلمس البنزين مباشرة.
- Abstraction (التجريد): بتعرف إن "دوس بنزين العربية تمشي"، ومش محتاج تعرف الحقن بيشتغل إزاي.
- Inheritance (الوراثة): "عربية نقل" هي "عربية" بكل خصايصها، وزيادة عليها صندوق.
- Polymorphism (تعدد الأشكال): كل العربيات عندها
Start()، بس العربية الكهربا بتبدأ بطريقة والبنزين بطريقة تانية.
public abstract class PaymentMethod // (1) abstraction
{
private decimal _balance; // (2) encapsulation
protected PaymentMethod(decimal openingBalance)
{
ArgumentOutOfRangeException.ThrowIfNegative(openingBalance);
_balance = openingBalance;
}
public decimal Balance => _balance;
protected bool Debit(decimal amount)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);
if (_balance < amount) return false;
_balance -= amount;
return true;
}
public abstract Task<bool> PayAsync(decimal amount); // what, not how
}
public sealed class Wallet : PaymentMethod // (3) inheritance
{
public Wallet(decimal openingBalance) : base(openingBalance) { }
public override Task<bool> PayAsync(decimal amount) // (4) polymorphism
{
return Task.FromResult(Debit(amount));
}
}
PaymentMethod method = new Wallet(150m);
await method.PayAsync(100m); // true; balance is now 50
await method.PayAsync(100m); // false; insufficient funds
نتتبّع المثال: new Wallet(150m) بيعمل محفظة برصيد 150؛ الحرف m بيحدد إن الرقم decimal عشري. أول دفع بقيمة 100 ينجح ويترك 50. المحاولة التانية بقيمة 100 ترجع false والرصيد يفضل 50، لأن التحقق حصل قبل الخصم. الجزء اللي بينادي الدفع يتعامل مع عقد PaymentMethod، بينما كائن Wallet هو اللي ينفذ العملية. Task.FromResult يغلف نتيجة جاهزة؛ المثال لا ينفّذ اتصالًا بخدمة دفع.
- (1) الـ abstract class بيعرّف "إيه" المطلوب (
PayAsync)، من غير "إزاي". - (2) الـ
_balanceprivate، فمحدش من برّه يقدر يغيّره بالغلط. والتغيير بيعدّي علىDebitاللي فيها التحقق. - (3) الـ
WalletبتخصصPaymentMethodوتقدر تناديDebitالمحمية. الحقل_balanceيفضل private؛ الكلاس المشتق ما يقدرش يلمسه مباشرة، والـ constructor بيستدعي constructor الأب لتهيئة الرصيد. - (4) المتغيّر نوعه
PaymentMethod، بس اللي بيتنفذ هو method الـWallet. وده runtime polymorphism بـvirtualوoverride.
#Value types و Reference types و BoxingValue types, reference types and boxing
- عندك متغيران يشيران لنفس List فيها السعر 100. إضافة سعر من خلال أحدهما تظهر عند القراءة من الآخر؛ لأن القائمة نفسها واحدة، والمنسوخ هو المرجع إليها.
- أما لو عندك int قيمته 100 ونسخته لمتغير ثانٍ، زيادة الثاني إلى 101 تترك الأول 100. هنا كل متغير يحتفظ بقيمته.
- إذن اسأل: هل نسخت قيمة أم مرجعًا لكائن مشترك؟ مكان التخزين وحده لا يفسر الفرق، ونسخ struct يحتوي مرجعًا لا ينسخ الكائن المشار إليه.
لو نسخت Point وعدّلت النسخة، هل الأصل يتغير؟ ولو نسخت متغيراً يشير إلى Box، هل النسختان تشيران لنفس الكائن؟ المثال يجاوب عملياً، ثم يوضح لما قيمة زي int تتحول إلى object. مكان التخزين الدقيق تفصيل تنفيذ؛ الفرق المهم هنا سلوك النسخ والمعنى.
struct Point { public int X; } // value type
class Box { public int X; } // reference type
var p1 = new Point { X = 1 };
var p2 = p1; p2.X = 99; // copy → p1.X is still 1
var b1 = new Box { X = 1 };
var b2 = b1; b2.X = 99; // same object → b1.X is 99
object o = 5; // boxing: the int is copied into a new heap object
int n = (int)o; // unboxing: copied back
Value type (struct و int و decimal و DateTime و enum) | Reference type (class و string و array و delegate) | |
|---|---|---|
| التمثيل | القيمة نفسها | مرجع للكائن |
| الـ assignment | بينسخ القيمة كلها | بينسخ العنوان بس |
| null | لأ، إلا لو int? | أيوة |
| الوراثة | مفيش | أيوة |
إمتى struct؟ استخدمه غالباً لقيمة صغيرة تمثل شيئاً واحداً، مثل Point . مفيش حد 16 byte ثابت يصلح لكل الحالات: قِس حجم النسخ والاستخدام الفعلي، واستخدم class لما الهوية المشتركة مهمة.
Boxing: لما value type يتعامل كـ object أو interface، بيتعمل object جديد على الـ heap. وده وضغط على الـ ، فالـ generics (List<int>) بتمنعه.
#class و struct و recordclass vs struct vs record
في order system، الـ Order غالباً له هوية مستقلة حتى لو اتغيرت تفاصيله؛ أما مبلغ أو نقطة في الخريطة فقيمتهم هي بياناتهم. هنا بنقارن تمثيل الهوية والقيمة، وبعدها يوضح الكود كيف record يوفّر مقارنة بالقيم ونسخاً غير هدّام. اختيار النوع يعتمد على معنى الدومين، مش شكل syntax بس.
public record Money(decimal Amount, string Currency); // reference type, value equality
public readonly record struct Point(int X, int Y); // value type, value equality
var a = new Money(100, "EGP");
var b = new Money(100, "EGP");
Console.WriteLine(a == b); // True: compared by values
var c = a with { Amount = 150 }; // non-destructive copy
Console.WriteLine(a); // Money { Amount = 100, Currency = EGP }
| class | struct | record (class) | record struct | |
|---|---|---|---|---|
| النوع | reference | value | reference | value |
| المساواة الافتراضية | بالعنوان | بالقيم؛ التنفيذ الافتراضي قد يستخدم مقارنة مباشرة أو حسب الحالة، والتكلفة تعتمد على النوع والـ runtime | بالقيم (generated) | بالقيم (generated) |
| لأ | لأ (إلا readonly) | positional properties بـ init بس ممكن members تانية تبقى mutable | لأ (إلا readonly record struct) | |
| أحسن في | entities ليها هوية وسلوك | قيم صغيرة جداً | ، و events، و value objects | قيم صغيرة بمساواة |
#decimal و double و floatdecimal vs double vs float
الـ double بيخزّن الأرقام بالـ binary. ورقم زي 0.1 مالوش تمثيل دقيق بالـ binary، زي ما 1/3 مالوش تمثيل دقيق بالعشري (0.3333…). فبيحصل خطأ صغير.
أما الـ decimal فبيخزّن بالـ عشري (base-10)، فالـ 0.1 بيتخزن 0.1 بالظبط. وده المطلوب في الفلوس.
Console.WriteLine(0.1 + 0.2 == 0.3); // False (0.30000000000000004)
Console.WriteLine(0.1m + 0.2m == 0.3m); // True
float f = 10.5f; // 'f' suffix: a literal like 10.5 is a double by default
decimal d = 10.5m; // 'm' suffix; without it: compile error (no implicit double → decimal)
Math.Round(2.5m); // 2: banker's rounding (to even)
Math.Round(2.5m, MidpointRounding.AwayFromZero); // 3
| النوع | الحجم | الدقة | السرعة | تستخدمه في |
|---|---|---|---|---|
| float | 4 bytes | ~6-9 أرقام | سريع جداً | graphics، و sensors، و ML |
| double | 8 bytes | ~15-17 رقم | سريع (hardware) | علوم وإحصاء |
| decimal | 16 bytes | 28-29 رقم عشري | أبطأ (software) | الفلوس والمحاسبة |
Money)، والتقريب في مكان واحد بس، علشان النتايج تبقى ثابتة في كل السيستم.#interface و abstract classInterface vs abstract class
| interface | abstract class | |
|---|---|---|
| الوراثة المتعددة | ✅ الـ class يعمل implement لأكتر من واحد | ❌ base class واحد |
| Instance fields | ❌ | ✅ |
| Constructor | ❌ | ✅ |
| Implementation | default interface methods (C# 8+)، ونادراً | ✅ عادي |
| المعنى | "يقدر يعمل" (capability) | "هو نوع من" ومعاه كود مشترك |
القاعدة: ابدأ بـ interface (أمرن، وأسهل في الـ testing والـ )، واستخدم abstract class لما يبقى فيه كود و state مشتركين فعلاً.
public interface IRefundable { Task RefundAsync(decimal amount); } // capability
// shared skeleton + state
public abstract class ReportBase
{
protected readonly ILogger Log;
protected ReportBase(ILogger log) => Log = log;
public async Task<byte[]> ExportAsync() => Render(await QueryAsync());
protected abstract Task<IReadOnlyList<Row>> QueryAsync();
protected virtual byte[] Render(IReadOnlyList<Row> rows) => ExcelWriter.Write(rows);
}
#string و StringBuilder و Immutabilitystring, StringBuilder and immutability
الـ string في : أي "تعديل" بيعمل string جديد. وده بيخليه thread-safe، وآمن كـ key في Dictionary، وبيسمح بالـ interning.
string csv = "";
foreach (var o in orders)
csv += $"{o.Id},{o.Total}\n";var sb = new StringBuilder();
foreach (var o in orders)
sb.Append(o.Id).Append(',').Append(o.Total).AppendLine();
string csv = sb.ToString();
// or: string.Join("\n", orders.Select(o => $"{o.Id},{o.Total}"))الـ concatenation في سطر واحد عادي، والـ compiler بيحسّنه. لكن جوه loop استخدم StringBuilder أو string.Join.
#Delegates و Lambda و EventsDelegates, lambdas and events
بعد ما الطلب يتحفظ، ممكن خدمة أخرى تحتاج تعرف إن OrderPlaced حصل عشان تبدأ إرسال الإيميل. يحدد شكل دالة يمكن تمريرها، والـ طريقة مختصرة لكتابتها، والـ event يتيح للمهتمين الاشتراك من غير ما يرفعوا الحدث بأنفسهم. المثال يوضح السلسلة دي.
Func<decimal, decimal> addTax = price => price * 1.14m; // takes decimal, returns decimal
Action<string> log = msg => Console.WriteLine(msg); // returns nothing
Predicate<Order> isPaid = o => o.Status == "Paid"; // returns bool
public class OrderService
{
public event EventHandler<OrderPlacedArgs>? OrderPlaced;
public void Place(Order o)
{
// ... save
OrderPlaced?.Invoke(this, new OrderPlacedArgs(o.Id)); // only the owner raises it
}
}
service.OrderPlaced += (s, e) => email.Send(e.OrderId); // subscribe
// unsubscribe (-=) from long-lived publishers, or you leak memory
- الـ delegate متغيّر بيشيل method (function pointer آمن).
Func<…, TResult>: بيرجّع قيمة، وآخر type parameter هو الـ return.Action<…>: مابيرجّعش حاجة.Predicate<T>: بيرجّع bool، وهو نفسFunc<T, bool>.- event: delegate محمي، اللي برّه يقدر يعمل
+=و-=بس، ومايقدرش يعمل Invoke.
#Exceptions و using و IDisposableExceptions, using and IDisposable
طلب دفع ممكن يترفض بسبب الرصيد أو يفشل مؤقتاً لأن gateway غير متاحة، والملف المفتوح لازم يتقفل حتى لو حصل خطأ. الكود يفرق بين معالجة حالة معروفة، تسجيل فشل نعيد رميه، وتنضيف الموارد. بنستخدم exception للحالات الاستثنائية، مش كمسار عادي لكل نتيجة متوقعة.
try
{
await payment.ChargeAsync(order, ct);
}
catch (PaymentDeclinedException ex) when (ex.Code == "insufficient_funds") // (1) filter
{
return Result.Fail("payment.declined");
}
catch (HttpRequestException ex)
{
log.LogWarning(ex, "Gateway unreachable for {OrderId}", order.Id);
throw; // (2) keep stack trace
}
finally
{
metrics.RecordAttempt(); // (3) always runs
}
await using var file = File.OpenWrite("report.csv"); // (4) disposed at end of scope
- (1) Exception filter (
when): بتمسك الـ exception بس لو الشرط اتحقق. - (2)
throw;بيحافظ على الـ stack الأصلي. أماthrow ex;فبيمسحه، فبتضيع معلومة فين المشكلة حصلت. - (3)
finallyبيتنفذ في كل الحالات. - (4)
usingبيضمن إنDisposeيتنده حتى لو حصل exception، وawait usingلـIAsyncDisposable.
Exception عام وتتجاهله. وفي الـ استخدم global exception handler.#Generics و Constraints و VarianceGenerics, constraints and variance
- مطلوب تخزن آخر ثلاث نتائج: مرة أرقام ومرة أسماء. بدل ما تكتب حاويتين بنفس المنطق، تكتب حاوية بنوع T يختاره المستدعي.
- عند استخدام List
، الإضافة تتوقع int والقراءة ترجع int؛ إضافة اسم نصي ترفضها أداة الترجمة. ده معنى generics: نفس البنية مع نوع محدد وآمن. - لو كودك محتاج ينادي دالة من عقد معين على T، تضع constraint يعلن الشرط. ابدأ بفهم T والشرط؛ variance موضوع لاحق عن التحويل بين بعض الأنواع العامة.
لو عندك قائمة IDs للأوامر، عايز نفس قواعد القائمة سواء الـ ID رقم أو نص من غير نسخ الكود أو تحويل كل قيمة لـ object. Generics بتحافظ على النوع وقت الـ compile؛ constraints بتقول إيه العمليات المسموحة على النوع العام. Variance بتشرح إمتى التحويل بين أنواع عامة آمن.
public interface IRepository<T> where T : class, IEntity // constraints
{
Task<T?> GetAsync(Guid id, CancellationToken ct);
}
public static T Max<T>(T a, T b) where T : IComparable<T> => a.CompareTo(b) >= 0 ? a : b;
IEnumerable<string> names = new List<string>();
IEnumerable<object> objects = names; // covariance (out T): safe for reading
Action<object> printAny = o => Console.WriteLine(o);
Action<string> printText = printAny; // contravariance (in T): safe for input
- Generics: كود واحد لأي type، مع type safety وقت الـ compile، ومن غير casts ولا .
- Constraints (
where):class، وstruct، وnew()، وnotnull، و interface، و base class. - Covariance (
out T): تستخدم type أخص مكان الأعم، في الإخراج بس (IEnumerable<out T>). - Contravariance (
in T): تستخدم type أعم مكان الأخص، في الإدخال بس (Action<in T>). والـ variance بتشتغل مع interfaces و بس، مش classes.
أسئلة الانترفيو
مفيش نتايج
راجع فهمك
ليه الاختيار ده مناسب؟ إمتى ممكن ماينفعش؟ وإيه اللي هيتغير لو الحمل زاد أو ظهر فشل؟
2.1What are the four pillars of OOP?Junior
Encapsulation و Abstraction و Inheritance و Polymorphism، وعرّف كل واحد في جملة ومعاه مثال.
Encapsulation hides internal state and exposes controlled behavior. Abstraction exposes what something does, not how. Inheritance lets a class reuse and extend a base class. Polymorphism lets the same call behave differently depending on the runtime type, through virtual methods or interfaces. In practice I prefer composition and interfaces over deep inheritance hierarchies.
2.2What is the difference between a class and a struct?Junior
الـ class reference type: عند assignment بننسخ المرجع، فيقدر متغيران يشاوروا لنفس الكائن. الـ struct value type: بننسخ القيمة، بما فيها أي references جواها؛ ده مش deep copy للكائنات المشار ليها. مكان التخزين يتوقف على الاستخدام وتحسينات الـ runtime، مش قاعدة stack مقابل heap. الكلاس يقدر يرث base class واحدة؛ الـ struct ما يقدرش يرث struct أو class أخرى، لكنه يقدر يطبق interfaces. أختار struct لقيمة صغيرة مناسبة للنسخ، وغالبًا ، وأقيس تكلفة النسخ والـ بدل حد حجم إلزامي.
A class is a reference type: assignment copies a reference, so two variables can refer to the same object. A struct is a value type: assignment copies its value, including any reference fields, rather than deep-copying their objects. Storage depends on context and runtime optimization, not a universal stack-versus-heap rule. A class can inherit one base class; a struct cannot inherit another struct or class, but can implement interfaces. I consider structs for small, preferably values and measure copying and costs rather than treating a size guideline as a language limit.
2.3What is a record? How is it different from a class?Mid
الـ record بيولّد value equality و ToString و with لنسخة سطحية. في positional record class الـ properties بتبقى init، إنما positional record struct العادي properties بتاعته mutable؛ لازم readonly record struct عشان تبقى read-only. والـ record ممكن يبقى فيه members mutable، فمش تلقائياً. استخدمه للـ والـ events والـ value objects.
A record generates value equality, a useful ToString, and a shallow non-destructive copy with with. Positional record classes have init-only properties; positional record structs are mutable unless declared readonly. Records can also contain mutable members, so they aren't automatically . I use records for , messages and value objects, and classes for entities with identity and behavior. See the official record reference.
2.4Interface vs abstract class: when would you use each?Junior
الـ interface عقد "يقدر يعمل"، والكلاس ممكن يطبق أكتر من واحد. الـ interface ما يقدرش يعلن instance fields. أما الـ abstract class فيقدر يحتوي حالة خاصة بكل كائن وكود مشترك وconstructor، والكلاس ليه base class واحدة. ابدأ بالـ interface لما المطلوب عقد بين الأجزاء.
An interface defines a capability; a class can implement many, and an interface cannot declare instance fields. An abstract class is a partial base type: it can have instance state, constructors and shared implementation, but a class inherits only one. I default to interfaces for flexibility and testability, and use an abstract class when several types share code, like a base report that defines the export flow.
2.5What are the access modifiers in C#?Junior
| Modifier | مين يقدر يوصله |
|---|---|
| public | أي حد |
| private | جوه الـ type بس، وده الافتراضي للـ members |
| protected | الـ type والـ subclasses |
| internal | نفس الـ assembly، وده الافتراضي للـ top-level types |
| protected internal | نفس الـ assembly أو أي subclass |
| private protected | الـ subclasses في نفس الـ assembly بس |
| file | نفس الملف بس (C# 11)، وغالباً مع source generators |
public is visible everywhere; private only inside the type, the default for members; protected inside the type and derived types; internal within the same assembly, the default for top-level types; protected internal means same assembly or any derived type; private protected means derived types in the same assembly; and file, since C# 11, limits a type to its source file. In modular code, internal is how a module hides its implementation.
2.6What is the difference between const, readonly and static readonly?Junior
الـ const قيمة وقت الـ compile، وبتتنسخ في الكود اللي بيستخدمها (لو غيّرتها في library، اللي بيستخدمها لازم rebuild)، وبتدعم أنواع ثوابت محددة زي الأرقام وbool وchar وstring وenum. أما الـ readonly فبيتحدد وقت التشغيل في الـ constructor، وكل instance ليه قيمته. والـ static readonly قيمة واحدة للـ type، بتتحدد مرة وقت التشغيل.
A const is a compile-time constant baked into every caller, so changing it in a library requires recompiling consumers, and it supports constant-compatible types such as numbers, booleans, characters, strings and enums. A readonly field is assigned at runtime in its declaration or constructor and never changes afterwards; each instance can have its own value. static readonly is one runtime value per type, which I use for things like TimeSpans or shared instances.
2.7What does the static keyword mean? What is a static class?Junior
الـ static member بيتبع الـ type مش الـ object، فنسخة واحدة. والـ static class مايتعملوش instance ولا وراثة، وكل members فيه static (زي Math أو extension methods). وتجنّب الـ static state المتغيّر: خطر في الـ threads، وصعب في الـ testing.
A static member belongs to the type rather than an instance, so there's a single copy. A static class can't be instantiated or inherited and contains only static members, like Math or a class of extension methods. I avoid mutable static state because it's shared across threads and hard to test; shared services are registered as singletons in instead.
2.8What is the difference between ref, out and in?Mid
void Increment(ref int x) => x++; // initialized; read + write
bool TryParse(string s, out int value) { ... } // method must assign it
double Distance(in Point a, in Point b) { ... } // by reference, read-only
الـ ref لازم ليه قيمة قبل الاستدعاء، والـ method تقرا وتكتب. والـ out الـ method لازم تحط فيه قيمة، وده pattern الـ TryParse. والـ in by reference بس read-only، علشان ماتنسخش struct كبير.
All three pass by reference. ref requires the variable to be initialized and lets the method read and modify it. out doesn't need initialization, but the method must assign it, which is the TryParse pattern. in passes by reference as read-only, mainly to avoid copying large structs. In modern code I often return a tuple or a result type instead of using out parameters.
2.9decimal vs double vs float: which one for money and why?Junior
decimal للفلوس، لأنه base-10، فالـ 0.1 بيتخزن بالظبط. أما الـ double و الـ float فـ binary، وفيهم خطأ تقريب (0.1 + 0.2 != 0.3). والـ suffixes: m للـ decimal و f للـ float.
For money I always use decimal. It's a base-10 type with 28 to 29 significant digits, so values like 0.1 are exact. float and double are binary floating point: fast and with a huge range, but 0.1 + 0.2 isn't exactly 0.3, which is unacceptable for currency. decimal is slower and uses 16 bytes, so scientific and graphics code uses double. The literal suffixes are m for decimal and f for float; an unsuffixed literal like 10.5 is a double.
2.10How does Math.Round work by default?Mid
افتراضياً banker's rounding (ToEven): الـ .5 بيقرّب للزوجي، يعني 2.5 بتبقى 2، و 3.5 بتبقى 4. وده بيقلل الانحياز في المجاميع. ولو البيزنس عايز التقريب المدرسي، حدد AwayFromZero.
By default Math.Round uses banker's rounding: midpoints go to the nearest even number, so 2.5 becomes 2 and 3.5 becomes 4, which avoids systematic bias when summing many values. If the business expects 2.5 to become 3, I pass MidpointRounding.AwayFromZero explicitly, and keep all rounding in one place such as a Money type.
2.11What is boxing and unboxing?Junior
الـ تحويل value type لـ object أو interface، فبيتعمل object جديد على الـ heap. والـ الرجوع. وده و pressure، فالـ generics بتمنعه.
wraps a value type in a heap object when it's treated as object or as an interface; copies it back out. Each box is an , so in hot loops or non-generic collections like ArrayList it adds pressure. Generics avoid it because List of int stores the ints directly.
2.12What are generics? What are constraints?Junior
كود واحد يشتغل مع أي type، مع type safety ومن غير . والـ constraints (where) بتحدد الـ T لازم يكون إيه.
Generics let me write one type-safe implementation for many types, without casts or , like List of T or a generic repository. Constraints with the where keyword restrict T to a class or struct, types with a parameterless constructor, non-nullable types, or types implementing an interface, which lets me call members such as CompareTo on T safely.
2.13What are covariance and contravariance?Senior
الـ Covariance (out): الـ IEnumerable<string> ينفع مكان IEnumerable<object> لأنه بيطلّع بس. والـ Contravariance (in): الـ Action<object> ينفع مكان Action<string> لأنه بياخد بس. وده للـ interfaces والـ بس.
Covariance, marked out T, lets a generic interface or that only produces T be treated as producing a base type: an of string is an IEnumerable of object. Contravariance, marked in T, lets something that only consumes T accept a more derived type: an Action of object works as an Action of string. It applies only to interfaces and delegates, and only where it's type-safe, which is why List of T is .
2.14What are Func, Action and Predicate?Junior
جاهزة: Func بيرجّع قيمة، و Action مابيرجّعش، و Predicate<T> بيرجّع bool.
They're built-in generic types. Func takes up to sixteen inputs and returns a value, with the last type parameter as the return type. Action takes inputs and returns nothing. Predicate of T takes a T and returns a bool, the same as Func of T and bool. are usually assigned to them, and methods accept them.
2.15What is the difference between a delegate and an event?Mid
الـ event محمي: اللي برّه يعمل subscribe و unsubscribe بس، ومايقدرش يعمل invoke ولا يمسح المشتركين. وده Observer pattern. والفخ: نسيان الـ unsubscribe من publisher عايش طول الوقت بيعمل memory leak.
An event is a with restricted access: outside the declaring class you can only subscribe and unsubscribe, not invoke it or replace the subscriber list. That enforces the observer pattern, where only the publisher raises the event. A common pitfall is forgetting to unsubscribe from a long-lived publisher, which keeps subscribers alive and leaks memory.
2.16What are extension methods?Junior
public static class StringExtensions
{
public static string Truncate(this string value, int max) =>
value.Length <= max ? value : value[..max] + "…";
}
var title = product.Name.Truncate(40);
static method جوه static class، وأول parameter عليه this، فبتتنده كأنها method في الـ type. والـ كله extension methods. وفي C# 14 فيه extension members (بلوك extension)، ودول بيضيفوا properties كمان.
An extension method is a static method in a static class whose first parameter is marked with "this", so it can be called as if it were an instance method of that type, without modifying or inheriting from it. is built from extension methods. C# 14 adds extension members: an extension block can also declare extension properties and static members.
2.17What does the yield keyword do?Mid
yield return بيحوّل الـ method لـ iterator (state machine) بيطلّع قيمة واحدة كل مرة وقت ما تتطلب (lazy). فلو عملت Take(3) على مليون، 3 بس هيتحسبوا. و yield break بيوقف.
yield return turns a method into an iterator: the compiler generates a state machine that produces one value at a time, only when the caller asks for the next element. Nothing runs until enumeration starts, so in a pipeline like Take(3) only three values are computed. It's ideal for streaming large or infinite sequences without building a list. yield break ends the sequence.
2.18What is the difference between throw and throw ex?Junior
throw; بيحافظ على الـ stack الأصلي. أما throw ex; فبيعمله reset من السطر ده، فبتضيع معلومة فين الخطأ حصل.
throw; rethrows the current exception and preserves its original stack . throw ex; resets the stack trace to the current line, so you lose where the error actually occurred. I use throw; to rethrow, or wrap it in a new exception with the original as the inner exception when I need to add context.
2.19How do you handle exceptions properly?Junior
امسك الـ exceptions المحددة اللي تقدر تتصرف فيها بس، و using و finally للتنضيف، وماتبلعش exceptions، ومفيش exceptions للحالات المتوقعة، و global handler في الـ بيرجّع ProblemDetails.
I catch only specific exceptions I can actually handle, use filters when a condition matters, and release resources with using or finally. I never swallow exceptions silently, and I don't use them for expected outcomes like not-found or validation failures; those return results. Rethrows use throw;. In an , a global exception handler logs details and returns a ProblemDetails response without leaking internals.
2.20What is the difference between == and Equals()? Why override GetHashCode?Mid
للـ reference types الـ == بيقارن العنوان (إلا لو اتعمله overload زي string). والـ Equals ممكن يتعمله override للقيم. ولو عملت override لـ Equals لازم GetHashCode كمان، لأن الـ Dictionary و HashSet بيدوّروا بالـ hash الأول.
For reference types, == compares references by default unless the type overloads it, as string does. Equals can be overridden for value equality. If you override Equals you must override GetHashCode consistently, because Dictionary and HashSet find the bucket by hash first; equal objects with different hash codes would never be found. Records generate both correctly.
2.21What is the difference between String and StringBuilder?Junior
الـ string ، وأي تعديل بيعمل string جديد. أما الـ StringBuilder فـ buffer بيتعدّل في مكانه، فاستخدمه في الـ loops.
string is , so each concatenation allocates a new string; in a loop that means repeated copying. StringBuilder keeps a resizable buffer and appends in place, so I use it, or string.Join, when building text in a loop. For a few concatenations, interpolation is fine.
2.22What does the using statement do?Junior
بيضمن إن Dispose() يتنده في الآخر حتى لو حصل exception، والـ compiler بيحوّله لـ try/finally. وفيه using var (لآخر الـ scope)، و await using.
using guarantees Dispose is called on an IDisposable at the end of the block, even if an exception occurs; the compiler turns it into try/finally. The using declaration form disposes at the end of the enclosing scope, and await using calls DisposeAsync. I use it for connections, streams, files and anything holding unmanaged resources.
2.23What are nullable value types and nullable reference types?Junior
int? value type يقبل null. والـ nullable reference types (C# 8+، ومفعّلة افتراضياً في المشاريع الجديدة): string معناها "مش null"، و string? معناها "ممكن null"، والـ compiler بيحذّرك. وده أكبر حماية من NullReferenceException.
int? is a nullable value type, short for Nullable of int. Nullable reference types, enabled by default in new projects, make the compiler track nullability: string should never be null and string? may be, and you get warnings when dereferencing something that might be null. I treat those warnings as errors and handle nulls with ?., ?? and ??=, plus C# 14's null-conditional assignment.
2.24What is reflection and when should you use it?Mid
بتفحص الـ types وتنده members وقت التشغيل، وده اللي بيستخدمه الـ والـ serializers والـ ORMs. وعيوبه: بطيء، ومن غير compile-time safety، ومش متوافق مع و trimming. فاستخدمه وقت الـ startup، واعمل cache، والبديل source generators.
lets code inspect types and invoke members at runtime; containers, serializers, ORMs and test frameworks rely on it. It's slower, loses compile-time safety and conflicts with trimming and Native . I use it mainly at startup, for example to scan an assembly and register handlers, cache the results, and prefer source generators on hot paths.
2.25What are expression trees?Senior
الكود كـ داتا: Expression<Func<Order, bool>> شجرة objects بتوصف الـ ، وده اللي بيخلي يحوّل لـ . أما الـ Func العادي فـ compiled code تقدر تنفذه بس.
An represents code as a data structure. When a is assigned to an Expression of Func, the compiler builds a tree describing it, which libraries can inspect and translate; that's how turns a Where on into a WHERE clause. A plain Func is compiled code that can only be executed.
2.26What is dynamic in C#?Mid
بيأجّل الـ type checking لوقت التشغيل. مفيد مع COM interop. بس مفيش IntelliSense ولا compile-time errors، وأبطأ. وفي الكود العادي تجنّبه، واستخدم JsonElement و JsonNode للـ المجهول.
dynamic defers member binding to runtime. It can simplify COM interop or loosely shaped data, but you lose IntelliSense and compile-time checks, errors surface at runtime and calls are slower. In application code I avoid it, using strong types or JsonElement and JsonNode for unknown .
2.27Why is immutability useful?Mid
الـ object اللي مابيتغيرش آمن تشاركه بين threads من غير locks، وأسهل في الفهم والـ testing، ومفيش side effects. والأدوات: record، و init، و readonly، و ImmutableArray، و FrozenDictionary.
objects can't change after creation, so they can be shared across threads without locks or , and they're easier to reason about and test because there are no hidden side effects. In C# I use records, init-only and required properties, readonly fields, and immutable or frozen collections.
2.28What is the difference between overloading and overriding?Junior
الـ Overloading نفس الاسم بـ parameters مختلفة، والاختيار وقت الـ compile. والـ Overriding الـ subclass بيغيّر method virtual أو abstract، والاختيار وقت التشغيل. أما new (hiding) فغير override، وغالباً bug.
Overloading means several methods with the same name but different parameters, resolved at compile time. Overriding means a derived class replaces a virtual or abstract method, resolved at runtime from the actual object. Hiding with the new keyword is different: a call through a base-typed variable still runs the base method, which usually signals a bug.
2.29What do sealed, virtual and abstract mean?Junior
virtual: الـ subclass ممكن يعمل override. و abstract: لازم يعمل override، ومفيش implementation. و sealed: مفيش وراثة أو override تاني. والـ sealed كمان بيساعد الـ يعمل devirtualization.
virtual means a derived class may override the member; abstract means it must, and the member has no body; sealed prevents further inheritance or overriding. I seal classes by default unless they're designed for inheritance: it documents intent and lets the devirtualize calls.
2.30What are required and init properties?Mid
init بيسمح بتعيين الـ property وقت الإنشاء بس. و required (C# 11) بيطلب من الـ caller يعيّنها، لكنه مش ضمان لصحة القيمة وقت التشغيل: ممكن تبقى null أو empty. دول مش بيعملوا deep ؛ مثلاً list متعينة بـ init تفضل قابلة للتعديل.
An init accessor allows assignment during initialization, while required makes the compiler require callers to assign the member. Neither guarantees a valid runtime value: a required property may be null or empty, and an init-only list can still be mutated. These are construction aids, not deep . See the official required reference.
اختبر نفسك
إجابات الاختبار وعلامات إكمال المحاور بتتحفظ على المتصفح ده، ومش بتتنقل لأجهزة تانية.
What does 0.1 + 0.2 == 0.3 return with double?
Which parameter modifier requires the method to assign a value?
Two records new Money(100, "EGP") are compared with ==. Result?
What is the difference between throw; and throw ex;?
throw; دايماً.You override Equals on a class used as a Dictionary key. What else must you do?
Which accessibility makes a type visible only inside its assembly?
Where is a struct stored when it is a field of a class?
Covariance (out T) allows...