Beginner C# Interview Questions and Answers

๐Ÿ“‹ Table of Contents โ–พ
  1. Questions & Answers
  2. 📝 Knowledge Check

💻 Beginner C# Interview Questions

This lesson covers the fundamental C# concepts every .NET developer must know. Master the type system, OOP principles, collections, LINQ, delegates, generics, exception handling, and core language features. These questions reflect what interviewers ask at junior and entry-level C# roles.

Questions & Answers

01 What is C# and what are its key features?

Core C# (C-Sharp) is a strongly-typed, object-oriented, general-purpose programming language developed by Microsoft as part of the .NET platform. It was designed by Anders Hejlsberg and first released in 2000. C# runs on the CLR (Common Language Runtime) and compiles to CIL (Common Intermediate Language) bytecode.

Key features:

  • Strongly typed โ€” every variable has a compile-time type; no implicit conversions between incompatible types
  • Object-oriented โ€” classes, inheritance, polymorphism, encapsulation
  • Type-safe โ€” the CLR enforces type rules at runtime; no raw pointer arithmetic by default
  • Managed memory โ€” garbage collector handles allocation and deallocation
  • LINQ โ€” integrated query syntax for collections, XML, databases, and more
  • async/await โ€” first-class asynchronous programming support
  • Generics โ€” type-safe reusable code without boxing overhead
  • Pattern matching โ€” expressive conditional logic on types and shapes
  • Cross-platform โ€” .NET 5+ runs on Windows, Linux, macOS
// Hello World in C#
Console.WriteLine("Hello, C#!");

// Current version: C# 13 (.NET 9), C# 12 (.NET 8 LTS)

02 What is the difference between value types and reference types in C#?

Type System

  • Value types โ€” stored on the stack (or inline in containing objects). Assignment copies the value. Include: int, double, bool, char, decimal, struct, enum.
  • Reference types โ€” the variable holds a reference (pointer) to heap memory. Assignment copies the reference, not the data. Both variables point to the same object. Include: class, string, array, delegate, interface.
// Value type -- copy semantics
int a = 10;
int b = a;    // b gets a COPY of a's value
b = 20;
Console.WriteLine(a); // 10 -- a is unchanged

// Reference type -- reference semantics
var list1 = new List<int> { 1, 2, 3 };
var list2 = list1;    // list2 points to the SAME object
list2.Add(4);
Console.WriteLine(list1.Count); // 4 -- list1 was mutated!

// struct = value type
struct Point { public int X, Y; }
var p1 = new Point { X = 1, Y = 2 };
var p2 = p1;      // copy
p2.X = 99;
Console.WriteLine(p1.X); // 1 -- p1 unchanged

// string is a reference type but is IMMUTABLE
// Reassignment creates a new string object, does not mutate
string s1 = "hello";
string s2 = s1;
s1 = "world";         // s1 now points to a new string
Console.WriteLine(s2); // "hello" -- s2 unchanged

03 What are the main data types in C#?

Type System

Integer: sbyte (8-bit signed), byte (8-bit unsigned), short/ushort (16-bit), int/uint (32-bit), long/ulong (64-bit), nint/nuint (native-size)

Floating-point: float (32-bit, ~7 digits), double (64-bit, ~15 digits), decimal (128-bit, 28-29 digits โ€” use for money)

Other: bool, char (UTF-16), string (immutable Unicode sequence)

int     i  = 42;
long    l  = 9_000_000_000L;
double  d  = 3.14159;
decimal m  = 19.99m;          // m suffix required for decimal
float   f  = 3.14f;           // f suffix required for float
bool    b  = true;
char    c  = 'A';
string  s  = "Hello";

// Type aliases map to .NET framework types
// int   = System.Int32
// long  = System.Int64
// string = System.String
// bool  = System.Boolean

// Checked arithmetic -- throws OverflowException instead of silently wrapping
checked { int overflow = int.MaxValue + 1; }  // throws

// sizeof
Console.WriteLine(sizeof(int));      // 4 bytes
Console.WriteLine(sizeof(double));   // 8 bytes

04 What is the difference between a class and a struct in C#?

OOP

  • class โ€” reference type, heap-allocated, supports inheritance, can be null, has default parameterless constructor, identity semantics
  • struct โ€” value type, stack-allocated (or inline), no inheritance (can implement interfaces), cannot be null (unless Nullable<T>), copied on assignment, value semantics
// Class -- reference type, heap allocated
class PersonClass
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// Struct -- value type, stack allocated
struct PersonStruct
{
    public string Name;
    public int Age;
}

// Behaviour difference
var pc1 = new PersonClass { Name = "Alice", Age = 30 };
var pc2 = pc1;      // pc2 references SAME heap object
pc2.Name = "Bob";
Console.WriteLine(pc1.Name); // "Bob" -- shared reference!

var ps1 = new PersonStruct { Name = "Alice", Age = 30 };
var ps2 = ps1;      // ps2 is a COPY
ps2.Name = "Bob";
Console.WriteLine(ps1.Name); // "Alice" -- original unchanged

// When to use struct:
// Small data (few fields), value semantics needed, no inheritance needed
// Performance-critical code (avoid heap allocation)
// Examples: System.DateTime, System.Point, System.Guid

// When to use class:
// Complex objects, inheritance needed, identity semantics

05 What are access modifiers in C#?

OOP

// public -- accessible everywhere
public class MyClass { public int Value; }

// private -- accessible only within the same class (default for members)
class MyClass { private int _secret; }

// protected -- accessible within the class and derived classes
class Base { protected int Data; }
class Derived : Base { void Use() { Data = 1; } } // OK

// internal -- accessible within the same assembly (.dll/.exe)
internal class Helper { }

// protected internal -- accessible in same assembly OR derived classes
protected internal void Method() { }

// private protected -- accessible in same class AND derived classes within same assembly
private protected void Method() { }

// file -- accessible only within the same source file (C# 11+)
file class FileLocalHelper { }

// Default access modifiers:
// class/struct/enum/interface (top-level): internal
// class members: private
// interface members: public (implicit)

06 How does inheritance work in C#?

OOP C# supports single inheritance for classes (a class can inherit from only one base class) but multiple interface implementation. All classes implicitly inherit from System.Object.

// Base class
public class Animal
{
    public string Name { get; set; }
    public Animal(string name) { Name = name; }

    public virtual string Speak() => "...";   // virtual = overridable
    public void Breathe() => Console.WriteLine("breathing");  // not overridable
}

// Derived class
public class Dog : Animal
{
    public Dog(string name) : base(name) { }  // call base constructor

    public override string Speak() => $"{Name} says Woof!"; // override
}

public class Cat : Animal
{
    public Cat(string name) : base(name) { }
    public override string Speak() => $"{Name} says Meow!";
}

// Polymorphism
Animal[] animals = { new Dog("Rex"), new Cat("Whiskers") };
foreach (var a in animals)
    Console.WriteLine(a.Speak()); // calls the correct override

// sealed -- prevent further inheritance
public sealed class MyFinalClass : Dog { }

// base keyword -- call base class member
public class GoldenRetriever : Dog
{
    public override string Speak() => base.Speak() + " (tail wagging)";
}

// abstract -- must be overridden by non-abstract subclass
public abstract class Shape
{
    public abstract double Area();     // no implementation
    public double Perimeter() => 0;   // optional concrete member
}

07 What are interfaces in C#? How do they differ from abstract classes?

OOP

// Interface -- contract (what) without implementation (how)
public interface IShape
{
    double Area();
    double Perimeter();
    string Colour { get; set; }      // property in interface

    // Default implementation (C# 8+) -- rarely used
    void Describe() => Console.WriteLine($"Area: {Area()}");
}

public interface IDrawable
{
    void Draw();
}

// Multiple interface implementation
public class Circle : IShape, IDrawable
{
    public double Radius { get; }
    public string Colour { get; set; } = "Red";

    public Circle(double r) { Radius = r; }
    public double Area()      => Math.PI * Radius * Radius;
    public double Perimeter() => 2 * Math.PI * Radius;
    public void Draw()        => Console.WriteLine("Drawing circle");
}

// Interface vs Abstract Class:
// Interface: pure contract, no state, multiple allowed, no constructor
// Abstract class: partial implementation, single inheritance, can have state/constructor

// When to use interface:  define a capability (ISortable, IDisposable, IComparable)
// When to use abstract:   provide a base with shared behaviour (Animal, Shape)

// interface segregation -- keep interfaces small and focused
public interface IReadable  { string Read(); }
public interface IWritable  { void Write(string data); }
public interface IReadWrite : IReadable, IWritable { }

08 What are properties in C# and how do they differ from fields?

OOP

public class Product
{
    // Field -- direct storage, typically private
    private decimal _price;

    // Full property -- explicit getter and setter
    public decimal Price
    {
        get { return _price; }
        set
        {
            if (value < 0) throw new ArgumentException("Price cannot be negative");
            _price = value;
        }
    }

    // Auto-property -- compiler generates backing field
    public string Name { get; set; } = "";

    // Read-only auto-property (only settable in constructor)
    public Guid Id { get; } = Guid.NewGuid();

    // Init-only property (C# 9+) -- settable during object initialisation only
    public string Sku { get; init; } = "";

    // Computed property (no backing field)
    public string DisplayName => $"{Name} (${Price:F2})";

    // Expression-bodied property
    public bool IsExpensive => Price > 100;
}

// Object initialiser syntax (uses setters or init)
var p = new Product
{
    Name = "Widget",
    Price = 9.99m,
    Sku = "WDG-001"
};

// Why properties over public fields?
// - Encapsulation: add validation without changing the public API
// - Can add logic later without breaking callers
// - Enables data binding, serialisation, and reflection to work correctly

09 What is the difference between an abstract class and an interface?

OOP

// Abstract class -- partial base with shared behaviour
public abstract class Logger
{
    private readonly string _prefix;

    protected Logger(string prefix) { _prefix = prefix; }  // constructor

    // Shared concrete method
    protected string FormatMessage(string msg) => $"[{_prefix}] {msg}";

    // Abstract -- subclass must implement
    public abstract void Log(string message);

    // Virtual -- subclass may override
    public virtual void LogError(string msg) => Log($"ERROR: {msg}");
}

public class FileLogger : Logger
{
    public FileLogger() : base("FILE") { }
    public override void Log(string msg) => File.AppendAllText("app.log", FormatMessage(msg));
}

// Interface -- pure contract
public interface ILogger
{
    void Log(string message);
    void LogError(string message);
}

// Key differences:
// Abstract class    -- can have fields, constructor, concrete members
//                   -- single inheritance only
// Interface         -- no instance fields (C# 8+ allows static fields)
//                   -- no constructor
//                   -- multiple implementation allowed
//                   -- C# 8+ supports default implementations (sparingly)

// Rule of thumb:
// Use abstract class when subclasses share code/state
// Use interface when defining a capability that unrelated types might share

10 What are the main collection types in C#?

Collections

using System.Collections.Generic;

// List<T> -- ordered, resizable array; O(1) index access; O(n) insert/remove
var list = new List<int> { 1, 2, 3 };
list.Add(4);
list.Insert(0, 0);
list.Remove(2);
list.Sort();

// Dictionary<TKey, TValue> -- hash map; O(1) average get/set
var dict = new Dictionary<string, int>();
dict["Alice"] = 95;
dict.TryGetValue("Bob", out var score);  // safe get (no exception)

// HashSet<T> -- unique values, O(1) Contains
var set = new HashSet<string> { "a", "b", "c" };
set.Add("a");          // ignored (already exists)
set.Contains("a");     // true, O(1)

// Queue<T> -- FIFO
var q = new Queue<int>();
q.Enqueue(1); q.Enqueue(2);
var item = q.Dequeue();    // 1

// Stack<T> -- LIFO
var s = new Stack<int>();
s.Push(1); s.Push(2);
var top = s.Pop();         // 2

// LinkedList<T> -- O(1) insert/remove at known node; O(n) index access
var ll = new LinkedList<int>(new[] { 1, 2, 3 });

// SortedDictionary<K,V> and SortedSet<T> -- always sorted, O(log n) operations
// ImmutableList, ImmutableDictionary (System.Collections.Immutable) -- thread-safe read-only

11 What is LINQ and how do you use it?

LINQ LINQ (Language Integrated Query) allows querying collections, databases, XML, and more using a consistent syntax integrated into C#. LINQ is lazy โ€” queries are not executed until the results are iterated.

var people = new List<Person>
{
    new("Alice", 30, "Engineering"),
    new("Bob",   25, "Marketing"),
    new("Carol", 35, "Engineering"),
};

// Method syntax (more common)
var engineers = people
    .Where(p => p.Dept == "Engineering")
    .OrderBy(p => p.Age)
    .Select(p => p.Name)
    .ToList();   // materialise the query

// Query syntax (SQL-like)
var result = from p in people
             where p.Dept == "Engineering"
             orderby p.Age
             select p.Name;

// Common LINQ operators
people.First(p => p.Age > 25);         // first match or exception
people.FirstOrDefault(p => p.Age > 50);// null if no match
people.Single(p => p.Name == "Alice");  // exactly one or exception
people.Count(p => p.Dept == "Engineering"); // 2
people.Any(p => p.Age > 40);           // false
people.All(p => p.Age > 18);           // true
people.Sum(p => p.Age);                 // 90
people.Average(p => p.Age);             // 30.0
people.Max(p => p.Age);                 // 35
people.Min(p => p.Age);                 // 25

// Grouping
var byDept = people.GroupBy(p => p.Dept);
foreach (var group in byDept)
    Console.WriteLine($"{group.Key}: {group.Count()}");

// Projection -- select new shape
var names = people.Select(p => new { p.Name, p.Age });

// Deferred execution -- query runs when iterated, not when defined
var query = people.Where(p => p.Age > 25); // not yet executed
people.Add(new("Dave", 40, "Engineering")); // Dave will be included
var list2 = query.ToList();                  // executed now, includes Dave

12 What are delegates and events in C#?

Delegates A delegate is a type-safe function pointer โ€” a variable that holds a reference to a method (or methods). Events are a publisher/subscriber pattern built on delegates.

// Declare a delegate type
delegate int MathOperation(int a, int b);

// Assign a method
MathOperation add = (a, b) => a + b;
MathOperation mul = (a, b) => a * b;

add(3, 4);   // 7

// Multicast delegate -- holds multiple methods
MathOperation both = add + mul;
both(3, 4);  // calls both; last result (12) is returned

// Built-in generic delegates (no custom delegate needed)
Func<int, int, int> sum    = (a, b) => a + b;   // returns int
Action<string>      print  = Console.WriteLine;  // returns void
Predicate<int>      isEven = n => n % 2 == 0;   // returns bool

// Events -- publisher/subscriber
public class Button
{
    // EventHandler<T> is a built-in delegate: void(object sender, T e)
    public event EventHandler<string>? Clicked;  // ? = nullable

    public void Click()
    {
        Clicked?.Invoke(this, "Button was clicked");  // null-safe invoke
    }
}

var btn = new Button();
btn.Clicked += (sender, message) => Console.WriteLine(message);  // subscribe
btn.Clicked += (sender, message) => LogClick(message);           // multiple subscribers
btn.Click();   // both handlers run

// Unsubscribe
btn.Clicked -= (sender, message) => Console.WriteLine(message);

13 What are generics in C#?

Generics Generics allow writing type-safe, reusable code with type parameters resolved at compile time. Unlike Java’s type erasure, C# generics are reified โ€” the actual type is known at runtime.

// Generic class
public class Stack<T>
{
    private readonly List<T> _items = new();

    public void Push(T item) => _items.Add(item);
    public T Pop()
    {
        if (_items.Count == 0) throw new InvalidOperationException("Stack is empty");
        var item = _items[^1];
        _items.RemoveAt(_items.Count - 1);
        return item;
    }
    public T Peek() => _items[^1];
    public int Count => _items.Count;
}

var intStack = new Stack<int>();
intStack.Push(1); intStack.Push(2);
intStack.Pop();   // 2 (no boxing, type-safe)

// Generic method
public T Max<T>(T a, T b) where T : IComparable<T>
    => a.CompareTo(b) >= 0 ? a : b;

Max(3, 7);       // 7
Max("apple", "banana"); // "banana"

// Generic constraints
public class Repository<T> where T : class, IEntity, new()
{
    // T must be a reference type, implement IEntity, and have a parameterless constructor
    public T CreateNew() => new T();
}

// Available constraints:
// where T : class          -- reference type
// where T : struct         -- value type
// where T : new()          -- has parameterless constructor
// where T : SomeClass      -- must inherit SomeClass
// where T : ISomeInterface -- must implement interface
// where T : unmanaged      -- unmanaged type (no references)

14 How does exception handling work in C#?

Exceptions

// try / catch / finally
try
{
    var result = int.Parse("abc");       // throws FormatException
    var item   = list[100];              // throws IndexOutOfRangeException
}
catch (FormatException ex)
{
    Console.WriteLine($"Format error: {ex.Message}");
}
catch (Exception ex) when (ex.Message.Contains("index"))  // exception filter
{
    Console.WriteLine("Index related error");
}
catch (Exception ex)
{
    Console.WriteLine($"Unexpected: {ex.GetType().Name}: {ex.Message}");
    throw;  // re-throw preserving stack trace (not: throw ex; which resets it)
}
finally
{
    Console.WriteLine("Always runs -- cleanup here");
}

// Custom exception
public class InsufficientFundsException : Exception
{
    public decimal Balance { get; }
    public decimal Amount  { get; }

    public InsufficientFundsException(decimal balance, decimal amount)
        : base($"Cannot withdraw {amount:C}; balance is {balance:C}")
    {
        Balance = balance;
        Amount  = amount;
    }
}

throw new InsufficientFundsException(100m, 500m);

// Exception hierarchy
// System.Exception
//   SystemException (runtime errors)
//     NullReferenceException, IndexOutOfRangeException, StackOverflowException
//   ApplicationException (app-specific, deprecated -- just extend Exception)
//   IOException, HttpRequestException, ...

15 What is boxing and unboxing in C#?

Type System Boxing wraps a value type in a heap-allocated object. Unboxing extracts the value back from the object. Both operations are implicit (boxing) and explicit (unboxing) but have performance costs.

// Boxing -- value type to object (heap allocation + copy)
int i = 42;
object obj = i;          // boxes i -- allocates on heap
Console.WriteLine(obj.GetType().Name); // "Int32"

// Unboxing -- object to value type (type check + copy)
int j = (int)obj;        // unboxes obj -- must cast to correct type
object wrongCast = (double)obj; // InvalidCastException!

// Performance impact
// Each box = heap allocation + GC pressure
// Tight loops with boxing can be 10-100x slower than generic alternatives

// ArrayList (old, non-generic) -- boxes every value type
var al = new ArrayList();
al.Add(42);    // boxes!
int k = (int)al[0]; // unboxes!

// List<int> (generic) -- NO boxing
var list = new List<int>();
list.Add(42);  // no boxing -- stored as int directly
int l = list[0]; // no unboxing

// Interfaces cause boxing too
IComparable boxed = 42;  // boxes int into IComparable
// Fix: use generic constraints (where T : IComparable<T>)

// Check for boxing via IL inspection or BenchmarkDotNet

16 What are nullable types and the null-related operators in C#?

Type System

// Nullable value types -- T? allows null for value types
int? age = null;
age.HasValue  // false
age.Value     // throws InvalidOperationException if null
age.GetValueOrDefault()     // 0
age.GetValueOrDefault(-1)   // -1

// Nullable reference types (C# 8+ -- enabled per-project)
// <Nullable>enable</Nullable> in .csproj
string? name = null;           // nullable reference type
string  required = "Alice";    // non-nullable (compiler warns if null assigned)

// Null-conditional operator ?.
string? city = person?.Address?.City;   // null if any step is null
int? len = name?.Length;                // null if name is null

// Null-coalescing operator ??
string display = name ?? "Anonymous";   // "Anonymous" if name is null

// Null-coalescing assignment ??=
name ??= "Default";   // assigns "Default" only if name is null

// Null-forgiving operator ! (override compiler warning -- use sparingly)
string definitelyNotNull = possiblyNull!;  // tell compiler "I know it's not null"

// Pattern matching with null
if (name is not null)
    Console.WriteLine(name.Length);

// Null parameter checking (C# 11+)
// ArgumentNullException.ThrowIfNull(name); -- replaces if (name == null) throw ...

17 What is the var keyword and when should you use it?

Syntax var is implicit typing โ€” the compiler infers the type from the right-hand side. The variable is still strongly typed at compile time; var is purely syntactic sugar.

// var -- compiler infers type at compile time (still strongly typed)
var i      = 42;              // int
var s      = "hello";         // string
var list   = new List<int>(); // List<int>
var dict   = new Dictionary<string, List<int>>(); // Dictionary<string,List<int>>

// Especially useful for long generic types
var lookup = new Dictionary<string, List<Tuple<int, string, DateTime>>>();

// Useful for anonymous types (REQUIRED -- no other way to declare)
var anon = new { Name = "Alice", Age = 30 };
Console.WriteLine(anon.Name); // "Alice"

// LINQ results (especially with complex projections)
var results = from p in people where p.Age > 25 select p;

// When NOT to use var:
// When the type is not obvious from context
var x = GetResult(); // BAD -- what type does GetResult return?
SomeType x = GetResult(); // BETTER -- type is clear

// When var makes code clearer:
var person = new Person("Alice", 30); // OK -- type is clear from right side

// int, string, bool -- many style guides prefer explicit for primitives
int count = 0;  // more readable than: var count = 0;

18 What is the using statement and IDisposable pattern?

Core using ensures that Dispose() is called on an IDisposable object when leaving the block โ€” even if an exception occurs. It is syntactic sugar for try/finally { obj.Dispose(); }.

// using statement -- auto-calls Dispose() on exit
using (var file = new StreamReader("data.txt"))
{
    var content = file.ReadToEnd();
}  // file.Dispose() called here -- stream closed

// Using declaration (C# 8+) -- scope is the enclosing block
using var conn = new SqlConnection(connectionString);
conn.Open();
// ... use conn ...
// conn.Dispose() called at end of method/scope

// Multiple resources
using var conn2  = new SqlConnection(connStr);
using var cmd    = new SqlCommand("SELECT ...", conn2);

// Implement IDisposable correctly
public class DatabaseContext : IDisposable
{
    private SqlConnection? _connection;
    private bool _disposed;

    public DatabaseContext(string connStr)
    {
        _connection = new SqlConnection(connStr);
        _connection.Open();
    }

    public void Dispose()
    {
        Dispose(disposing: true);
        GC.SuppressFinalize(this);  // no need for finaliser to run
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
                _connection?.Dispose();  // release managed resources
            // release unmanaged resources here
            _disposed = true;
        }
    }
}

19 What are string methods and the difference between string and StringBuilder?

Strings

string s = "  Hello, World!  ";

// Common string methods
s.Trim()                   // "Hello, World!"
s.ToUpper()                // "  HELLO, WORLD!  "
s.ToLower()
s.Contains("World")        // true
s.StartsWith("Hello")      // false (has leading spaces)
s.EndsWith("!")
s.Replace("World", "C#")   // "  Hello, C#!  "
s.Split(',')               // ["  Hello", " World!  "]
s.Substring(9, 5)          // "World"
s[9..14]                   // "World" (range indexing, C# 8+)
string.IsNullOrWhiteSpace(s)

// String interpolation and formatting
$"Name: {name,10}"         // right-align in 10 chars
$"Pi: {Math.PI:F2}"        // 3.14
$"Date: {DateTime.Now:yyyy-MM-dd}"

// String vs StringBuilder
// string is IMMUTABLE -- every "modification" creates a new string object
string result = "";
for (int i = 0; i < 10000; i++)
    result += i.ToString(); // 10000 allocations -- O(n^2) total memory

// StringBuilder is MUTABLE -- modifies in place
var sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
    sb.Append(i);           // one buffer, O(n) -- much faster
string final = sb.ToString();

// Rule: use StringBuilder for 3+ string concatenations in a loop
// string.Concat/string.Join/$ are fine for a few concatenations

20 What are enums in C#?

Core

// Basic enum (defaults to int, values 0, 1, 2...)
public enum DayOfWeek { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }

// Explicit values
public enum HttpStatus
{
    OK             = 200,
    Created        = 201,
    BadRequest     = 400,
    Unauthorized   = 401,
    NotFound       = 404,
    ServerError    = 500
}

// Underlying type can be changed
public enum Priority : byte { Low = 1, Medium = 2, High = 3 }

// Flags enum -- combinable with bitwise operators
[Flags]
public enum Permission
{
    None    = 0,
    Read    = 1,
    Write   = 2,
    Execute = 4,
    All     = Read | Write | Execute
}

Permission p = Permission.Read | Permission.Write;
p.HasFlag(Permission.Read)    // true
p.HasFlag(Permission.Execute) // false
p |= Permission.Execute;      // add Execute
p &= ~Permission.Write;       // remove Write

// Enum methods
DayOfWeek.Monday.ToString()     // "Monday"
(int)DayOfWeek.Monday           // 0
(DayOfWeek)5                    // Saturday
Enum.Parse<DayOfWeek>("Friday") // DayOfWeek.Friday
Enum.TryParse("Sunday", out DayOfWeek day); // safer
Enum.GetValues<DayOfWeek>()     // all values (C# 10+)

21 What are C# records and how do they differ from classes?

C# 9+ record types (C# 9+) are reference types with value-based equality semantics. The compiler auto-generates Equals, GetHashCode, ToString, and a with expression for non-destructive mutation.

// Positional record -- concise syntax (immutable by default)
public record Person(string FirstName, string LastName, int Age);

var p1 = new Person("Alice", "Smith", 30);
var p2 = new Person("Alice", "Smith", 30);
p1 == p2   // true (value equality, not reference equality)

// with expression -- non-destructive copy with modifications
var p3 = p1 with { Age = 31 };  // new Person("Alice", "Smith", 31)
// p1 is unchanged

// Auto-generated ToString
p1.ToString()  // "Person { FirstName = Alice, LastName = Smith, Age = 30 }"

// Destructuring
var (first, last, age) = p1;  // positional deconstruction

// record struct (C# 10+) -- value type record
public record struct Point(double X, double Y);

// Mutable record class
public record Product
{
    public string Name    { get; set; } = "";
    public decimal Price  { get; set; }
}

// When to use records:
// DTOs, value objects, immutable data, CQRS commands/queries
// When NOT to use: objects with behaviour, mutable entities

22 What is pattern matching in C#?

C# 8+ Pattern matching tests a value against a shape or condition and optionally extracts values โ€” replacing verbose if/else chains and is/as casts.

object shape = new Circle(5.0);

// Type pattern
if (shape is Circle c)
    Console.WriteLine($"Circle with radius {c.Radius}");

// Switch expression (C# 8+)
string describe = shape switch
{
    Circle  c when c.Radius > 10 => "Large circle",
    Circle  c                    => $"Circle r={c.Radius}",
    Rectangle { Width: var w, Height: var h } => $"Rect {w}x{h}",
    null                         => "Nothing",
    _                            => "Unknown shape"
};

// Property pattern
if (person is { Age: >= 18, Name: not null })
    Console.WriteLine("Adult with a name");

// Positional pattern (requires Deconstruct)
if (point is (0, 0)) Console.WriteLine("Origin");
if (point is (var x, var y)) Console.WriteLine($"{x},{y}");

// List patterns (C# 11+)
int[] arr = { 1, 2, 3, 4, 5 };
if (arr is [1, 2, ..])       Console.WriteLine("Starts with 1,2");
if (arr is [.., 4, 5])       Console.WriteLine("Ends with 4,5");
if (arr is [var first, ..])  Console.WriteLine($"First: {first}");

// Logical patterns: and, or, not
if (age is >= 18 and <= 65) Console.WriteLine("Working age");
if (value is null or 0)      Console.WriteLine("Empty");

📝 Knowledge Check

🧠 Quiz Question 1 of 5

What is the key difference between a value type and a reference type in C#?





🧠 Quiz Question 2 of 5

Why is boxing a performance concern in C# and how do generics solve it?





🧠 Quiz Question 3 of 5

What is the difference between an interface and an abstract class in C#?





🧠 Quiz Question 4 of 5

What is LINQ’s deferred execution and why does it matter?





🧠 Quiz Question 5 of 5

What is the key difference between a C# record and a class?