🔵 C# .NET
C# Complete Cheatsheet
OOP, LINQ, async/await, delegates, events — complete C# .NET reference.
📖 10 sections
⏱ 26 min read
✅ Quizzes included
🌙 Dark mode
01 Syntax & Types
CSC# basics
using System;
using System.Collections.Generic;
using System.Linq;

class Program {
    static void Main(string[] args) {
        // Variables
        int age = 22;
        double pi = 3.14159;
        string name = "Ali";
        bool active = true;
        var items = new List();  // type inferred

        // String interpolation
        Console.WriteLine($"Hello, {name}! Age: {age}");
        Console.WriteLine($"Pi = {pi:F2}");  // format to 2 decimals

        // Null safety (C# 8+)
        string? nullable = null;          // nullable reference type
        string safe = nullable ?? "default";
        int? maybeInt = null;
        int value = maybeInt ?? 0;
        string? upper = nullable?.ToUpper();  // null-conditional

        // Pattern matching (C# 9+)
        object obj = 42;
        if (obj is int n && n > 0) Console.WriteLine($"Positive int: {n}");

        // Switch expression (C# 8+)
        string label = age switch {
            < 13 => "child",
            < 18 => "teenager",
            < 65 => "adult",
            _    => "senior"
        };
    }
}
var
Type inferred at compile time. Still statically typed.
dynamic
Resolved at runtime. Avoid — loses type safety.
record
Immutable data type: record Person(string Name, int Age); (C# 9+)
using
Import namespace OR resource disposal (using var file = ...)
02 OOP Classes
CSClasses and OOP
public class BankAccount {
    private decimal balance;
    public string Owner { get; private set; }  // auto-property
    private static int _count = 0;

    // Constructor
    public BankAccount(string owner, decimal initial = 0) {
        Owner = owner;
        balance = initial;
        _count++;
    }

    // Properties
    public decimal Balance => balance;  // read-only computed
    public static int Count => _count;

    // Methods
    public void Deposit(decimal amount) {
        if (amount > 0) balance += amount;
    }
    public bool Withdraw(decimal amount) {
        if (amount > 0 && balance >= amount) { balance -= amount; return true; }
        return false;
    }

    // Override ToString
    public override string ToString() => $"{Owner}: {balance:C}";

    // Operator overloading
    public static BankAccount operator +(BankAccount a, BankAccount b)
        => new BankAccount(a.Owner, a.balance + b.balance);
}

// Object initializer
var acc = new BankAccount("Ali") { };  // then init props if public set
// Records (immutable by default, C# 9+)
record Point(double X, double Y);
var p1 = new Point(1.0, 2.0);
var p2 = p1 with { X = 5.0 };  // non-destructive mutation
03 Inheritance & Polymorphism
CSInheritance and polymorphism
// Abstract base class
public abstract class Shape {
    public string Color { get; set; } = "black";
    public abstract double Area();   // must implement
    public virtual string Describe() => $"{Color} shape";
}

public class Circle : Shape {
    public double Radius { get; }
    public Circle(double radius) => Radius = radius;
    public override double Area() => Math.PI * Radius * Radius;
    public override string Describe() => $"{base.Describe()}: Circle r={Radius}";
}

public sealed class Square : Shape {  // sealed: can't inherit further
    public double Side { get; }
    public Square(double side) => Side = side;
    public override double Area() => Side * Side;
}

// Polymorphism
Shape[] shapes = { new Circle(5), new Square(4) };
foreach (var shape in shapes)
    Console.WriteLine($"{shape.Describe()} area={shape.Area():F2}");

// Pattern matching with types (C# 7+)
foreach (var s in shapes) {
    string info = s switch {
        Circle c => $"Circle radius {c.Radius}",
        Square sq => $"Square side {sq.Side}",
        _ => "Unknown"
    };
}
04 Interfaces & Abstract
CSInterfaces
// Interface definition
public interface IRepository {
    T? GetById(int id);
    IEnumerable GetAll();
    void Add(T entity);
    void Update(T entity);
    void Delete(int id);
    int Count { get; }  // interface can have property declarations
}

public interface IExportable {
    byte[] Export(string format);
    default string GetDefaultFormat() => "json";  // C# 8+ default impl
}

// Implement multiple interfaces
public class UserRepository : IRepository, IExportable {
    private List _users = new();
    public User? GetById(int id) => _users.FirstOrDefault(u => u.Id == id);
    public IEnumerable GetAll() => _users;
    public void Add(User user) => _users.Add(user);
    public void Update(User user) { Delete(user.Id); Add(user); }
    public void Delete(int id) => _users.RemoveAll(u => u.Id == id);
    public int Count => _users.Count;
    public byte[] Export(string format) => System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(_users);
}

// Dependency injection pattern
public class UserService {
    private readonly IRepository _repo;
    public UserService(IRepository repo) => _repo = repo;  // inject!
}
05 Collections & LINQ
CSCollections and LINQ
using System.Collections.Generic;
using System.Linq;

// Collections
var list = new List { 1, 2, 3, 4, 5 };
list.Add(6); list.Remove(3); list.Sort(); list.Reverse();

var dict = new Dictionary { ["a"] = 1, ["b"] = 2 };
dict["c"] = 3;
if (dict.TryGetValue("a", out int val)) Console.WriteLine(val);

var set = new HashSet { 1, 2, 3, 2, 1 };  // {1, 2, 3}
var queue = new Queue(); queue.Enqueue("first"); queue.Dequeue();
var stack = new Stack(); stack.Push("top"); stack.Pop();

// LINQ — Language Integrated Query
var numbers = Enumerable.Range(1, 10);

var evens = numbers.Where(n => n % 2 == 0);
var squares = numbers.Select(n => n * n);
var bigSquares = numbers.Where(n => n > 3).Select(n => n * n).ToList();
var sum = numbers.Sum();
var max = numbers.Max();
var first = numbers.First(n => n > 5);  // 6
var sorted = numbers.OrderByDescending(n => n).ThenBy(n => n);
var groups = numbers.GroupBy(n => n % 2 == 0 ? "even" : "odd");
bool any = numbers.Any(n => n > 8);
bool all = numbers.All(n => n > 0);
var flat = lists.SelectMany(l => l);   // flatten

// LINQ query syntax (SQL-like)
var query = from n in numbers
            where n % 2 == 0
            orderby n descending
            select n * n;
06 Async/Await
CSAsync/Await
using System.Net.Http;
using System.Threading.Tasks;

public class DataService {
    private readonly HttpClient _http = new();

    // Async method returns Task
    public async Task FetchDataAsync(string url) {
        try {
            var response = await _http.GetAsync(url);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync();
        } catch (HttpRequestException ex) {
            throw new Exception($"Request failed: {ex.Message}", ex);
        }
    }

    // Parallel async operations
    public async Task<(string, string)> FetchBothAsync(string url1, string url2) {
        var t1 = FetchDataAsync(url1);
        var t2 = FetchDataAsync(url2);
        await Task.WhenAll(t1, t2);
        return (t1.Result, t2.Result);
    }

    // IAsyncEnumerable — async streaming (C# 8+)
    public async IAsyncEnumerable GenerateAsync() {
        for (int i = 0; i < 10; i++) {
            await Task.Delay(100);
            yield return i;
        }
    }
}

// Usage
await foreach (var item in service.GenerateAsync()) {
    Console.WriteLine(item);
}
💡
Always use Async suffix for async methods. Avoid async void (can't be awaited). Use CancellationToken for cancellable operations.
07 Delegates & Events
CSDelegates and Events
// Delegate — type-safe function pointer
public delegate int Operation(int a, int b);
Operation add = (a, b) => a + b;
Operation mul = (a, b) => a * b;
Console.WriteLine(add(3, 4));  // 7

// Built-in delegates
Func addFunc = (a, b) => a + b;
Action print = s => Console.WriteLine(s);
Predicate isEven = n => n % 2 == 0;

// Multicast delegate
Action greet = () => Console.Write("Hello ");
greet += () => Console.Write("World");
greet += () => Console.WriteLine("!");
greet();  // Hello World!

// Events
public class Button {
    public event EventHandler? Clicked;
    public void Click() => Clicked?.Invoke(this, EventArgs.Empty);
}

public class Form {
    public Form() {
        var btn = new Button();
        btn.Clicked += OnButtonClicked;   // subscribe
        btn.Click();
        btn.Clicked -= OnButtonClicked;   // unsubscribe
    }
    private void OnButtonClicked(object? sender, EventArgs e)
        => Console.WriteLine("Button clicked!");
}
08 Properties & Indexers
CSProperties and Indexers
public class Temperature {
    private double _celsius;

    // Full property with validation
    public double Celsius {
        get => _celsius;
        set {
            if (value < -273.15) throw new ArgumentException("Below absolute zero!");
            _celsius = value;
        }
    }

    // Computed property
    public double Fahrenheit => _celsius * 9 / 5 + 32;
    public double Kelvin     => _celsius + 273.15;

    // Auto-property with init (C# 9 — immutable after creation)
    public string Unit { get; init; } = "Celsius";
}

// Indexer
public class Matrix {
    private double[,] _data;
    public int Rows { get; }
    public int Cols { get; }

    public Matrix(int rows, int cols) {
        Rows = rows; Cols = cols;
        _data = new double[rows, cols];
    }

    // Indexer
    public double this[int row, int col] {
        get => _data[row, col];
        set => _data[row, col] = value;
    }
}

var m = new Matrix(3, 3);
m[0, 0] = 1.0;  // uses indexer
09 Exception Handling
CSException handling
// Try-catch-finally
try {
    int result = 10 / 0;  // DivideByZeroException
    string s = null;
    int len = s!.Length;  // NullReferenceException
} catch (DivideByZeroException ex) {
    Console.WriteLine($"Math: {ex.Message}");
} catch (NullReferenceException ex) when (ex.Message.Contains("null")) {
    // Exception filter (when clause)
    Console.WriteLine("Null reference with filter");
} catch (Exception ex) {
    Console.WriteLine($"General: {ex.Message}");
    throw;  // rethrow preserving stack trace
} finally {
    Console.WriteLine("Always runs");
}

// Custom exception
public class InsufficientFundsException : Exception {
    public decimal Amount { get; }
    public InsufficientFundsException(decimal amount)
        : base($"Need ${amount} more") => Amount = amount;
}

// Using (IDisposable — auto close/release)
using var file = File.OpenRead("data.txt");
// file.Dispose() called automatically when scope ends

// Throw expression (C# 7+)
string name = input ?? throw new ArgumentNullException(nameof(input));
10 Mini Quizzes
❓ Quiz 1
What does 'record' do in C# 9+?
C# records create immutable reference types where equality is based on values, not references. record Person(string Name, int Age) auto-generates constructor, properties, Equals, GetHashCode and ToString.
❓ Quiz 2
What is the difference between IEnumerable and IQueryable in LINQ?
IEnumerable: executes in memory (all data loaded first). IQueryable: translates LINQ to SQL — the database does the filtering. Always use IQueryable when querying databases (Entity Framework) to avoid loading everything into memory.