Back to Blog
C# Developer Roadmap 2026: Skills, Tools & Path to Becoming a Pro
.Net/C#

C# Developer Roadmap 2026: Skills, Tools & Path to Becoming a Pro

Mihadul IslamNovember 19, 202565 min read

Summary

Complete C# roadmap for 2026. Learn C# from zero to hero with clear steps, skills, tools, and projects to become a job-ready .NET developer.

Your Complete Journey from Beginner to Professional with Special Focus on C# 14 Revolutionary Features

💡 This comprehensive roadmap covers everything from programming basics to mastering the groundbreaking C# 14 features (Extension Members, field keyword, and more) that will define modern C# development in 2026.

Table of Contents

Foundation Phase

Core C# Phase

Specialization Phase

LEVEL 0 – Programming Fundamentals

⚠️ Skip to Level 1 if: You already know C/C++ or another programming language

📖 Step 0.1 – Core Programming Logic

Learn fundamental programming concepts in any beginner-friendly language:

Core Concepts

  • ✅ What is a program, compiler, and runtime

  • ✅ Variables and data types

  • ✅ Input/output operations (Console.ReadLine(), Console.WriteLine())

  • ✅ Conditional statements (if/else)

  • ✅ Loop structures (for, while, do-while)

  • ✅ Functions and methods

  • ✅ Basic algorithms and problem-solving

Practice Problems

Solve 30-50 basic problems on:

  • HackerRank (Easy level)

  • Codeforces (Div. 3/4)

  • LeetCode (Easy)

Problem Categories:

  • Sum of numbers, averages

  • Finding max/min values

  • Factorial calculation

  • Prime number detection

  • Pattern printing (stars, pyramids)

  • Simple array operations

Resources


🛠️ LEVEL 1 – Development Environment & Version Control

🖥️ Step 1.1 – Setup Development Environment

Install Required Tools

1. .NET SDK

  • Download latest LTS version (.NET 10 recommended)

  • Verify installation: dotnet --version

2. IDE/Editor

  • Visual Studio 2025/2026 (Community Edition) - Full-featured IDE with C# 14 support

  • OR VS Code + C# Dev Kit extension - Lightweight option

  • Important: Ensure your IDE supports C# 14 features (Extension Members, field keyword)

3. SQL Server

  • SQL Server Express (for local development)

  • OR SQL Server LocalDB

  • Azure Data Studio or SSMS for database management

Your First C# Program

// Create project
dotnet new console -n HelloWorld
cd HelloWorld

// Program.cs
Console.WriteLine("Hello, C# World!");

Run your program:

dotnet run

Resources


📦 Step 1.2 – Version Control with Git

Why Git?

  • Track code changes over time

  • Collaborate with teams

  • Revert to previous versions

  • Create experimental branches safely

Essential Git Concepts

Repository Types:

  • Local: Your computer

  • Remote: GitHub, GitLab, Azure DevOps

Commit Process:

  • One-step: Direct commit (SVN style)

  • Two-step: Stage → Commit (Git style)

Core Git Commands

# Initialize repository
git init

# Clone existing repository
git clone <url>

# Check status
git status

# Stage changes
git add <file>
git add .  # Stage all

# Commit changes
git commit -m "Descriptive message"

# View history
git log
git log --oneline

Working with Remotes

# Add remote
git remote add origin <url>

# Push to remote
git push origin main

# Pull from remote
git pull origin main

# Fetch without merging
git fetch origin

Authentication Methods

  • HTTPS: Username + Personal Access Token

  • SSH: SSH key pairs (more secure, no password prompts)

Branching & Merging

# Create branch
git branch feature-login

# Switch to branch
git checkout feature-login
# OR (newer syntax)
git switch feature-login

# Create and switch in one command
git checkout -b feature-login

# Merge branch
git checkout main
git merge feature-login

# Delete branch
git branch -d feature-login

Handling Merge Conflicts

When conflicts occur:

  1. Git marks conflicted files

  2. Open files and look for conflict markers:

<<<<<<< HEAD
Your changes
=======
Their changes
>>>>>>> branch-name
  1. Manually resolve conflicts

  2. Stage resolved files: git add <file>

  3. Complete merge: git commit

Practice Tasks

  • ✅ Create GitHub account

  • ✅ Create repository for learning projects

  • ✅ Push at least 5 commits

  • ✅ Create a branch, make changes, merge back

  • ✅ Practice resolving a merge conflict

Resources


💻 LEVEL 2 – C# Syntax & Basic Types

🔢 Step 2.1 – Data Types & Variables

Value Types vs Reference Types

Value Types (stored on stack)

  • int, double, decimal, float, bool, char

  • struct, enum

  • Store actual value

Reference Types (stored on heap)

  • string, object, array

  • class, interface, delegate

  • Store reference to memory location

Built-in Types

// Integer types
byte age = 25;              // 0 to 255
short year = 2025;          // -32,768 to 32,767
int count = 1000;           // -2.1B to 2.1B
long population = 8000000000L;

// Floating-point types
float rate = 3.14f;         // 7 digits precision
double precise = 3.14159265; // 15-16 digits
decimal money = 99.99m;     // 28-29 digits (financial)

// Other types
bool isActive = true;
char grade = 'A';
string name = "John Doe";

Variables & Constants

// Variables (can change)
int counter = 0;
counter = 10;

// Constants (cannot change)
const double PI = 3.14159;
const int MAX_USERS = 100;

// Read-only (set once, at runtime)
readonly string ConnectionString;

Arrays

Single-Dimensional Arrays:

// Declaration and initialization
int[] numbers = new int[5];
int[] scores = { 85, 90, 78, 92, 88 };

// Access elements
scores[0] = 95;
int firstScore = scores[0];

// Array length
int length = scores.Length;

Multidimensional Arrays:

// 2D array (matrix)
int[,] matrix = new int[3, 4];
int[,] grid = {
    { 1, 2, 3 },
    { 4, 5, 6 },
    { 7, 8, 9 }
};

// Access
int value = grid[1, 2]; // 6

Jagged Arrays (Array of Arrays):

int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[] { 1, 2, 3, 4 };
jaggedArray[1] = new int[] { 5, 6 };
jaggedArray[2] = new int[] { 7, 8, 9 };

Console Input/Output

// Output
Console.WriteLine("Hello World!");
Console.Write("No newline");

// String interpolation
string name = "Alice";
int age = 30;
Console.WriteLine($"Name: {name}, Age: {age}");

// Input
Console.WriteLine("Enter your name:");
string userName = Console.ReadLine();

// Parsing input
Console.WriteLine("Enter a number:");
int number = int.Parse(Console.ReadLine());
// OR (safer)
if (int.TryParse(Console.ReadLine(), out int result))
{
    Console.WriteLine($"You entered: {result}");
}

Practice Projects

  • ✅ Calculator (basic arithmetic)

  • ✅ Grade calculator (input scores, calculate average)

  • ✅ Array manipulation (sum, average, max, min)

  • ✅ Temperature converter (C to F and vice versa)

Resources


🔀 Step 2.2 – Statements & Operators

Conditional Statements

If-Else:

int score = 85;

if (score >= 90)
{
    Console.WriteLine("Grade: A");
}
else if (score >= 80)
{
    Console.WriteLine("Grade: B");
}
else if (score >= 70)
{
    Console.WriteLine("Grade: C");
}
else
{
    Console.WriteLine("Grade: F");
}

Switch Statement:

string dayOfWeek = "Monday";

switch (dayOfWeek)
{
    case "Monday":
    case "Tuesday":
    case "Wednesday":
    case "Thursday":
    case "Friday":
        Console.WriteLine("Weekday");
        break;
    case "Saturday":
    case "Sunday":
        Console.WriteLine("Weekend");
        break;
    default:
        Console.WriteLine("Invalid day");
        break;
}

Operators

Arithmetic Operators:

int a = 10, b = 3;
int sum = a + b;        // 13
int diff = a - b;       // 7
int product = a * b;    // 30
int quotient = a / b;   // 3 (integer division)
int remainder = a % b;  // 1 (modulo)

// Increment/Decrement
a++;  // Post-increment
++a;  // Pre-increment
b--;  // Post-decrement
--b;  // Pre-decrement

Relational Operators:

bool isEqual = (a == b);       // false
bool isNotEqual = (a != b);    // true
bool isGreater = (a > b);      // true
bool isLess = (a < b);         // false
bool isGreaterOrEqual = (a >= b);
bool isLessOrEqual = (a <= b);

Logical Operators:

bool result1 = true && false;  // AND - false
bool result2 = true || false;  // OR - true
bool result3 = !true;          // NOT - false

// Short-circuit evaluation
bool x = false && ExpensiveOperation(); // ExpensiveOperation not called

Bitwise Operators:

int x = 5;  // 0101 in binary
int y = 3;  // 0011 in binary

int and = x & y;   // 0001 = 1
int or = x | y;    // 0111 = 7
int xor = x ^ y;   // 0110 = 6
int not = ~x;      // Inverts all bits
int leftShift = x << 1;   // 1010 = 10
int rightShift = x >> 1;  // 0010 = 2

Ternary Operator:

int age = 18;
string status = (age >= 18) ? "Adult" : "Minor";

Loop Structures

For Loop:

for (int i = 0; i < 10; i++)
{
    Console.WriteLine($"Iteration {i}");
}

// Loop through array
int[] numbers = { 1, 2, 3, 4, 5 };
for (int i = 0; i < numbers.Length; i++)
{
    Console.WriteLine(numbers[i]);
}

While Loop:

int counter = 0;
while (counter < 5)
{
    Console.WriteLine(counter);
    counter++;
}

Do-While Loop:

int num;
do
{
    Console.WriteLine("Enter a positive number:");
    num = int.Parse(Console.ReadLine());
} while (num <= 0);

Foreach Loop:

string[] fruits = { "Apple", "Banana", "Orange" };
foreach (string fruit in fruits)
{
    Console.WriteLine(fruit);
}

Break & Continue:

// Break - exit loop
for (int i = 0; i < 10; i++)
{
    if (i == 5) break;
    Console.WriteLine(i); // 0, 1, 2, 3, 4
}

// Continue - skip iteration
for (int i = 0; i < 10; i++)
{
    if (i % 2 == 0) continue;
    Console.WriteLine(i); // 1, 3, 5, 7, 9
}

Practice Projects

  • ✅ Pattern printing (pyramids, diamonds)

  • ✅ Number guessing game

  • ✅ Menu-driven calculator

  • ✅ Prime number checker

  • ✅ Fibonacci sequence generator

Resources


📝 Step 2.3 – Working with Text

String Fundamentals

string message = "Hello, World!";

// Properties
int length = message.Length;  // 13

// Indexing
char first = message[0];      // 'H'
char last = message[^1];      // '!' (index from end, C# 8+)

Essential String Methods

string text = "  Hello World  ";

// Case conversion
string upper = text.ToUpper();           // "  HELLO WORLD  "
string lower = text.ToLower();           // "  hello world  "

// Trimming
string trimmed = text.Trim();            // "Hello World"
string trimStart = text.TrimStart();     // "Hello World  "
string trimEnd = text.TrimEnd();         // "  Hello World"

// Searching
bool contains = text.Contains("World");  // true
int index = text.IndexOf("World");       // 8
int lastIndex = text.LastIndexOf("o");   // 10

// Substring
string sub = text.Substring(2, 5);       // "Hello"

// Replace
string replaced = text.Replace("World", "C#");  // "  Hello C#  "

// Split
string csv = "Apple,Banana,Orange";
string[] fruits = csv.Split(',');        // ["Apple", "Banana", "Orange"]

// Join
string joined = string.Join(", ", fruits);  // "Apple, Banana, Orange"

// Checking
bool isEmpty = string.IsNullOrEmpty(text);        // false
bool isWhitespace = string.IsNullOrWhiteSpace("   ");  // true

String Concatenation vs Interpolation

string firstName = "John";
string lastName = "Doe";
int age = 30;

// Concatenation (old way)
string msg1 = "Name: " + firstName + " " + lastName + ", Age: " + age;

// Interpolation (modern, preferred)
string msg2 = $"Name: {firstName} {lastName}, Age: {age}";

// Multi-line interpolation
string msg3 = $@"
Name: {firstName} {lastName}
Age: {age}
Status: Active
";

String Formatting

double price = 1234.56;
DateTime now = [DateTime.Now](<http://DateTime.Now>);

// Number formatting
string currency = $"{price:C}";          // $1,234.56
string decimal2 = $"{price:F2}";         // 1234.56
string percent = $"{0.85:P}";            // 85.00%

// Date formatting
string date1 = $"{now:yyyy-MM-dd}";      // 2025-11-20
string date2 = $"{now:dd/MM/yyyy}";      // 20/11/2025
string time = $"{now:HH:mm:ss}";         // 02:21:51
string full = $"{now:yyyy-MM-dd HH:mm}"; // 2025-11-20 02:21

Char Operations

char ch = 'A';

// Checking
bool isLetter = char.IsLetter(ch);       // true
bool isDigit = char.IsDigit('5');        // true
bool isUpper = char.IsUpper(ch);         // true
bool isLower = char.IsLower(ch);         // false
bool isWhiteSpace = char.IsWhiteSpace(' '); // true

// Conversion
char upper = char.ToUpper('a');          // 'A'
char lower = char.ToLower('A');          // 'a'

StringBuilder (Performance)

using System.Text;

// Bad for many concatenations (creates many string objects)
string result = "";
for (int i = 0; i < 1000; i++)
{
    result += i.ToString(); // Slow!
}

// Good - StringBuilder is mutable
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
    sb.Append(i);
}
string result = sb.ToString();

// StringBuilder methods
sb.AppendLine("New line");
sb.Insert(0, "Start: ");
sb.Remove(0, 7);
sb.Replace("old", "new");
sb.Clear();

Practice Projects

  • ✅ Palindrome checker

  • ✅ Word counter

  • ✅ Text-based menu system

  • ✅ String reverser

  • ✅ Caesar cipher encoder/decoder

  • ✅ Email validator (basic)

Resources


🏗️ LEVEL 3 – Object-Oriented Programming

📦 Step 3.1 – Classes & Objects Fundamentals

What are Classes and Objects?

Class: Blueprint for creating objects

Object: Instance of a class

// Define a class
public class Student
{
    // Fields (private by convention)
    private string name;
    private int age;
    
    // Properties (public interface)
    public string Name { get; set; }
    public int Age { get; set; }
    
    // Constructor
    public Student(string name, int age)
    {
        Name = name;
        Age = age;
    }
    
    // Method
    public void PrintInfo()
    {
        Console.WriteLine($"Student: {Name}, Age: {Age}");
    }
}

// Create and use objects
Student student1 = new Student("Alice", 20);
Student student2 = new Student("Bob", 22);

student1.PrintInfo();  // Student: Alice, Age: 20

Namespaces

namespace MyCompany.ProjectName.Models
{
    public class Product
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

// Using namespaces
using MyCompany.ProjectName.Models;

Product product = new Product();

Access Modifiers

public class Example
{
    public int PublicField;        // Accessible everywhere
    private int PrivateField;      // Only within this class
    protected int ProtectedField;  // This class + derived classes
    internal int InternalField;    // Within same assembly
    protected internal int ProtectedInternalField;  // Protected OR internal
    private protected int PrivateProtectedField;    // Protected AND internal
}

Properties

Auto-Implemented Properties:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    // Read-only auto-property
    public string Id { get; }
    
    // Init-only property (C# 9+)
    public DateTime BirthDate { get; init; }
}

var person = new Person 
{ 
    Name = "John", 
    Age = 30,
    BirthDate = new DateTime(1995, 1, 1)
};

Full Properties with Backing Field:

public class BankAccount
{
    private decimal _balance;
    
    public decimal Balance
    {
        get { return _balance; }
        set 
        { 
            if (value >= 0)
                _balance = value;
            else
                throw new ArgumentException("Balance cannot be negative");
        }
    }
}

Expression-Bodied Properties:

public class Circle
{
    public double Radius { get; set; }
    
    // Computed property
    public double Area => Math.PI * Radius * Radius;
    public double Circumference => 2 * Math.PI * Radius;
}

Method Overloading

public class Calculator
{
    // Same method name, different parameters
    public int Add(int a, int b)
    {
        return a + b;
    }
    
    public double Add(double a, double b)
    {
        return a + b;
    }
    
    public int Add(int a, int b, int c)
    {
        return a + b + c;
    }
}

Constructors

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
    
    // Default constructor
    public Product()
    {
        Category = "General";
    }
    
    // Parameterized constructor
    public Product(string name, decimal price)
    {
        Name = name;
        Price = price;
        Category = "General";
    }
    
    // Constructor chaining
    public Product(string name, decimal price, string category) 
        : this(name, price)  // Calls other constructor first
    {
        Category = category;
    }
}

Destructor/Finalizer

public class Resource
{
    // Finalizer (rarely needed in modern C#)
    ~Resource()
    {
        // Cleanup code
        Console.WriteLine("Resource being finalized");
    }
}

Readonly vs Const

public class Configuration
{
    // Const - compile-time constant
    public const int MAX_SIZE = 100;
    
    // Readonly - runtime constant
    public readonly string ConnectionString;
    
    public Configuration(string connString)
    {
        ConnectionString = connString;  // Can set in constructor
    }
}

Practice Projects

  • ✅ Student management system (Student class with properties and methods)

  • ✅ Product catalog (Product, Category classes)

  • ✅ Bank account simulator (Account class with deposit/withdraw)

  • ✅ Library system (Book, Member classes)

Resources


🎭 Step 3.2 – Advanced Class Types

Static Classes & Members

// Static class (cannot be instantiated)
public static class MathHelper
{
    public static double PI = 3.14159;
    
    public static double CalculateCircleArea(double radius)
    {
        return PI * radius * radius;
    }
}

// Usage
double area = MathHelper.CalculateCircleArea(5);

// Regular class with static members
public class Counter
{
    private static int count = 0;  // Shared across all instances
    
    public int InstanceId { get; }
    
    public Counter()
    {
        count++;
        InstanceId = count;
    }
    
    public static int GetTotalCount() => count;
}

Abstract Classes

// Cannot be instantiated directly
public abstract class Shape
{
    public string Color { get; set; }
    
    // Abstract method (must be implemented by derived classes)
    public abstract double CalculateArea();
    
    // Concrete method (can be inherited as-is)
    public void Display()
    {
        Console.WriteLine($"Shape Color: {Color}, Area: {CalculateArea()}");
    }
}

public class Circle : Shape
{
    public double Radius { get; set; }
    
    // Must implement abstract method
    public override double CalculateArea()
    {
        return Math.PI * Radius * Radius;
    }
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    public override double CalculateArea()
    {
        return Width * Height;
    }
}

Interfaces

// Interface defines contract
public interface IDrawable
{
    void Draw();
    void Erase();
}

public interface IResizable
{
    void Resize(double factor);
}

// Class can implement multiple interfaces
public class Shape : IDrawable, IResizable
{
    public void Draw()
    {
        Console.WriteLine("Drawing shape");
    }
    
    public void Erase()
    {
        Console.WriteLine("Erasing shape");
    }
    
    public void Resize(double factor)
    {
        Console.WriteLine($"Resizing by factor {factor}");
    }
}

Method Overriding

public class Animal
{
    // Virtual method can be overridden
    public virtual void MakeSound()
    {
        Console.WriteLine("Some generic animal sound");
    }
}

public class Dog : Animal
{
    // Override virtual method
    public override void MakeSound()
    {
        Console.WriteLine("Woof!");
    }
}

public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Meow!");
    }
}

// Usage
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.MakeSound();  // Woof!
animal2.MakeSound();  // Meow!

Hiding Members with 'new'

public class BaseClass
{
    public void Display()
    {
        Console.WriteLine("Base Display");
    }
}

public class DerivedClass : BaseClass
{
    // Hides base method (not override)
    public new void Display()
    {
        Console.WriteLine("Derived Display");
    }
}

// Usage
DerivedClass obj = new DerivedClass();
obj.Display();  // "Derived Display"

BaseClass baseRef = new DerivedClass();
baseRef.Display();  // "Base Display" (calls base version)

Sealed Keyword

// Sealed class cannot be inherited
public sealed class FinalClass
{
    public void DoSomething() { }
}

// This would cause error:
// public class CannotDerive : FinalClass { }

// Sealed method cannot be overridden further
public class Base
{
    public virtual void Method() { }
}

public class Middle : Base
{
    public sealed override void Method() { }
}

public class Derived : Middle
{
    // This would cause error:
    // public override void Method() { }
}

Partial Classes

// File1.cs
public partial class Employee
{
    public string Name { get; set; }
    public void Work()
    {
        Console.WriteLine("Working...");
    }
}

// File2.cs
public partial class Employee
{
    public decimal Salary { get; set; }
    public void TakeBreak()
    {
        Console.WriteLine("Taking a break...");
    }
}

// Both parts combined at compile time

Practice Projects

  • ✅ Shape hierarchy (Shape, Circle, Rectangle, Triangle with IDrawable)

  • ✅ Animal kingdom (Animal base, various animals with specific behaviors)

  • ✅ Payment system (IPayment interface, CreditCard, PayPal, BankTransfer)

  • ✅ Logger system (ILogger interface, FileLogger, ConsoleLogger, DatabaseLogger)

Resources


📊 Step 3.3 – Other Important Types

Struct vs Class

// Struct - value type
public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

// Class - reference type
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// Behavior difference
Point p1 = new Point(10, 20);
Point p2 = p1;  // Copies value
p2.X = 100;
Console.WriteLine(p1.X);  // Still 10

Person person1 = new Person { Name = "John" };
Person person2 = person1;  // Copies reference
[person2.Name](<http://person2.Name>) = "Jane";
Console.WriteLine([person1.Name](<http://person1.Name>));  // Changed to "Jane"

When to use struct:

  • Small data structures (< 16 bytes)

  • Immutable data

  • Represents a single value (like Point, Color, DateTime)

Enums

// Define enum
public enum OrderStatus
{
    Pending,      // 0
    Processing,   // 1
    Shipped,      // 2
    Delivered,    // 3
    Cancelled     // 4
}

// With explicit values
public enum ErrorCode
{
    Success = 0,
    NotFound = 404,
    ServerError = 500,
    Unauthorized = 401
}

// Flags enum (for combinations)
[Flags]
public enum FileAccess
{
    None = 0,
    Read = 1,
    Write = 2,
    Execute = 4,
    ReadWrite = Read | Write  // 3
}

// Usage
OrderStatus status = OrderStatus.Processing;
if (status == OrderStatus.Delivered)
{
    Console.WriteLine("Order delivered!");
}

// Convert to/from string
string statusName = status.ToString();  // "Processing"
OrderStatus parsed = Enum.Parse<OrderStatus>("Shipped");

// Flags usage
FileAccess access = [FileAccess.Read](<http://FileAccess.Read>) | FileAccess.Write;
bool canRead = access.HasFlag([FileAccess.Read](<http://FileAccess.Read>));  // true

DateTime

// Current date and time
DateTime now = [DateTime.Now](<http://DateTime.Now>);           // Local time
DateTime utcNow = DateTime.UtcNow;     // UTC time
DateTime today = [DateTime.Today](<http://DateTime.Today>);        // Date only (time = 00:00:00)

// Create specific date
DateTime date1 = new DateTime(2025, 11, 20);
DateTime date2 = new DateTime(2025, 11, 20, 14, 30, 0);

// Operations
DateTime tomorrow = now.AddDays(1);
DateTime nextWeek = now.AddDays(7);
DateTime nextMonth = now.AddMonths(1);
DateTime lastYear = now.AddYears(-1);

// Comparison
TimeSpan difference = tomorrow - now;
bool isBefore = date1 < date2;

// Formatting
string formatted1 = now.ToString("yyyy-MM-dd");          // 2025-11-20
string formatted2 = now.ToString("dd/MM/yyyy HH:mm:ss"); // 20/11/2025 02:21:51
string formatted3 = now.ToString("MMMM dd, yyyy");       // November 20, 2025

// Parsing
DateTime parsed = DateTime.Parse("2025-11-20");
if (DateTime.TryParse("invalid", out DateTime result))
{
    Console.WriteLine(result);
}

Parameter Modifiers

ref - Pass by reference:

void Increment(ref int number)
{
    number++;
}

int value = 10;
Increment(ref value);
Console.WriteLine(value);  // 11

out - Output parameter:

bool TryDivide(int a, int b, out int result)
{
    if (b == 0)
    {
        result = 0;
        return false;
    }
    result = a / b;
    return true;
}

if (TryDivide(10, 2, out int quotient))
{
    Console.WriteLine(quotient);  // 5
}

params - Variable number of arguments:

int Sum(params int[] numbers)
{
    return numbers.Sum();
}

int total1 = Sum(1, 2, 3);           // 6
int total2 = Sum(1, 2, 3, 4, 5, 6);  // 21

in - Read-only reference (C# 7.2+):

void ProcessLargeStruct(in LargeStruct data)
{
    // Can read but not modify
    Console.WriteLine(data.Value);
}

Tuples

// Named tuple
(string Name, int Age) person = ("John", 30);
Console.WriteLine([person.Name](<http://person.Name>));  // John

// Tuple return value
(int Min, int Max) GetRange(int[] numbers)
{
    return (numbers.Min(), numbers.Max());
}

var range = GetRange(new[] { 5, 2, 8, 1, 9 });
Console.WriteLine($"Min: {range.Min}, Max: {range.Max}");

// Tuple deconstruction
(int min, int max) = GetRange(new[] { 5, 2, 8, 1, 9 });
Console.WriteLine($"Min: {min}, Max: {max}");

Nullable Types

// Nullable value types
int? nullableInt = null;
DateTime? nullableDate = null;

// Check for null
if (nullableInt.HasValue)
{
    int value = nullableInt.Value;
}

// Null-coalescing operator
int result = nullableInt ?? 0;  // Use 0 if null

// Null-coalescing assignment (C# 8+)
nullableInt ??= 10;  // Assign 10 if null

Anonymous Types

var person = new 
{ 
    Name = "Alice", 
    Age = 25,
    City = "New York"
};

Console.WriteLine($"{[person.Name](<http://person.Name>)} is {person.Age} years old");

// Often used with LINQ
var results = students
    .Select(s => new { [s.Name](<http://s.Name>), s.Grade })
    .ToList();

Dynamic Type

dynamic value = 10;
value = "Hello";
value = new { Name = "Test" };

// No compile-time checking (use carefully!)
Console.WriteLine([value.Name](<http://value.Name>));

Practice Projects

  • ✅ Age calculator using DateTime

  • ✅ Deadline reminder system

  • ✅ File permission system using Flags enum

  • ✅ Status tracking system using enums

Resources


🔧 Step 3.4 – Generics & Collections

Generic Classes

// Generic class
public class Box<T>
{
    private T _content;
    
    public void Put(T item)
    {
        _content = item;
    }
    
    public T Get()
    {
        return _content;
    }
}

// Usage
Box<int> intBox = new Box<int>();
intBox.Put(42);
int value = intBox.Get();

Box<string> stringBox = new Box<string>();
stringBox.Put("Hello");

Generic Methods

public class Utilities
{
    public static T FindMax<T>(T[] array) where T : IComparable<T>
    {
        T max = array[0];
        foreach (T item in array)
        {
            if (item.CompareTo(max) > 0)
                max = item;
        }
        return max;
    }
}

// Usage
int[] numbers = { 5, 2, 8, 1, 9 };
int max = Utilities.FindMax(numbers);

Generic Constraints

// where T : class - must be reference type
public class ClassConstraint<T> where T : class
{
}

// where T : struct - must be value type
public class StructConstraint<T> where T : struct
{
}

// where T : new() - must have parameterless constructor
public class Repository<T> where T : new()
{
    public T Create()
    {
        return new T();
    }
}

// where T : BaseClass - must inherit from BaseClass
public class DerivedConstraint<T> where T : Animal
{
}

// where T : IInterface - must implement interface
public class InterfaceConstraint<T> where T : IComparable
{
}

// Multiple constraints
public class MultiConstraint<T> where T : class, IDisposable, new()
{
}

Default Keyword for Generics

public T GetDefaultValue<T>()
{
    return default(T);  // 0 for int, null for reference types, etc.
}

Generic Collections

List<T>:

List<string> names = new List<string>();
names.Add("Alice");
names.Add("Bob");
names.AddRange(new[] { "Charlie", "David" });

// Access
string first = names[0];
int count = names.Count;

// Search
bool contains = names.Contains("Alice");
int index = names.IndexOf("Bob");

// Remove
names.Remove("Alice");
names.RemoveAt(0);

// Iterate
foreach (string name in names)
{
    Console.WriteLine(name);
}

// LINQ operations
var filtered = names.Where(n => n.StartsWith("D")).ToList();

Dictionary<TKey, TValue>:

Dictionary<string, int> ages = new Dictionary<string, int>();
ages.Add("Alice", 25);
ages["Bob"] = 30;  // Add or update

// Access
int aliceAge = ages["Alice"];

// Safe access
if (ages.TryGetValue("Charlie", out int charlieAge))
{
    Console.WriteLine(charlieAge);
}

// Check existence
bool hasAlice = ages.ContainsKey("Alice");
bool hasAge30 = ages.ContainsValue(30);

// Iterate
foreach (KeyValuePair<string, int> kvp in ages)
{
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}

// Or
foreach (var (name, age) in ages)
{
    Console.WriteLine($"{name}: {age}");
}

HashSet<T>:

HashSet<int> numbers = new HashSet<int>();
numbers.Add(1);
numbers.Add(2);
numbers.Add(2);  // Duplicate, not added
// numbers = {1, 2}

// Set operations
HashSet<int> set1 = new HashSet<int> { 1, 2, 3 };
HashSet<int> set2 = new HashSet<int> { 3, 4, 5 };

set1.UnionWith(set2);        // {1, 2, 3, 4, 5}
set1.IntersectWith(set2);    // {3}
set1.ExceptWith(set2);       // {1, 2}

Queue<T>:

Queue<string> queue = new Queue<string>();
queue.Enqueue("First");
queue.Enqueue("Second");
queue.Enqueue("Third");

string first = queue.Dequeue();  // "First" (FIFO)
string peek = queue.Peek();      // "Second" (doesn't remove)

Stack<T>:

Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);

int top = stack.Pop();    // 3 (LIFO)
int peek = stack.Peek();  // 2 (doesn't remove)

Non-Generic Collections (Avoid)

// ArrayList - avoid, use List<T> instead
ArrayList list = new ArrayList();
list.Add(1);
list.Add("string");  // No type safety!

// Hashtable - avoid, use Dictionary<TKey, TValue> instead
Hashtable table = new Hashtable();

Practice Projects

  • ✅ Generic repository for CRUD operations

  • ✅ In-memory database using Dictionary and List

  • ✅ Task manager using appropriate collections

  • ✅ Contact book with search functionality

Resources


🔌 Step 3.5 – Important Interfaces

IEnumerable & IEnumerable<T>

// IEnumerable allows foreach iteration
public class CustomCollection : IEnumerable<int>
{
    private List<int> _items = new List<int>();
    
    public void Add(int item) => _items.Add(item);
    
    public IEnumerator<int> GetEnumerator()
    {
        return _items.GetEnumerator();
    }
    
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

// Usage
CustomCollection collection = new CustomCollection();
collection.Add(1);
collection.Add(2);

foreach (int item in collection)
{
    Console.WriteLine(item);
}

ICollection & ICollection<T>

// ICollection adds Count, Add, Remove, Contains
public class MyCollection<T> : ICollection<T>
{
    private List<T> _items = new List<T>();
    
    public int Count => _items.Count;
    public bool IsReadOnly => false;
    
    public void Add(T item) => _items.Add(item);
    public bool Remove(T item) => _items.Remove(item);
    public bool Contains(T item) => _items.Contains(item);
    public void Clear() => _items.Clear();
    
    public void CopyTo(T[] array, int arrayIndex)
    {
        _items.CopyTo(array, arrayIndex);
    }
    
    public IEnumerator<T> GetEnumerator() => _items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

IDisposable

// For resources that need cleanup
public class FileProcessor : IDisposable
{
    private FileStream _fileStream;
    private bool _disposed = false;
    
    public FileProcessor(string filePath)
    {
        _fileStream = File.OpenRead(filePath);
    }
    
    public void ProcessFile()
    {
        // Process the file
    }
    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;
        
        if (disposing)
        {
            // Dispose managed resources
            _fileStream?.Dispose();
        }
        
        // Dispose unmanaged resources
        
        _disposed = true;
    }
    
    ~FileProcessor()
    {
        Dispose(false);
    }
}

// Using statement automatically calls Dispose
using (var processor = new FileProcessor("data.txt"))
{
    processor.ProcessFile();
}  // Dispose called automatically

// Or with C# 8+ syntax
using var processor2 = new FileProcessor("data.txt");
processor2.ProcessFile();
// Dispose called at end of scope

ICloneable

public class Person : ICloneable
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Address Address { get; set; }
    
    // Shallow copy
    public object Clone()
    {
        return this.MemberwiseClone();
    }
    
    // Deep copy (custom method)
    public Person DeepClone()
    {
        return new Person
        {
            Name = [this.Name](<http://this.Name>),
            Age = this.Age,
            Address = new Address 
            { 
                Street = this.Address.Street,
                City = [this.Address.City](<http://this.Address.City>)
            }
        };
    }
}

Practice Projects

  • ✅ Custom collection implementing IEnumerable

  • ✅ File reader/writer with proper IDisposable implementation

  • ✅ Database connection wrapper with using pattern

Resources


🏛️ Step 3.6 – OOP Principles & SOLID

Four Pillars of OOP

1. Encapsulation:

public class BankAccount
{
    // Hide implementation details
    private decimal _balance;
    
    // Controlled access through properties/methods
    public decimal Balance 
    { 
        get => _balance;
        private set => _balance = value; 
    }
    
    public void Deposit(decimal amount)
    {
        if (amount > 0)
            _balance += amount;
    }
    
    public bool Withdraw(decimal amount)
    {
        if (amount > 0 && amount <= _balance)
        {
            _balance -= amount;
            return true;
        }
        return false;
    }
}

2. Inheritance:

// Base class
public class Vehicle
{
    public string Brand { get; set; }
    public void Start() => Console.WriteLine("Vehicle started");
}

// Derived class inherits from base
public class Car : Vehicle
{
    public int NumberOfDoors { get; set; }
    public void OpenTrunk() => Console.WriteLine("Trunk opened");
}

3. Abstraction:

// Hide complex implementation, show only essentials
public interface IPaymentProcessor
{
    void ProcessPayment(decimal amount);
}

public class CreditCardProcessor : IPaymentProcessor
{
    public void ProcessPayment(decimal amount)
    {
        // Complex credit card processing logic hidden
        ValidateCard();
        ConnectToGateway();
        ChargeCard(amount);
        SendConfirmation();
    }
    
    private void ValidateCard() { }
    private void ConnectToGateway() { }
    private void ChargeCard(decimal amount) { }
    private void SendConfirmation() { }
}

4. Polymorphism:

public abstract class Shape
{
    public abstract void Draw();
}

public class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

public class Square : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a square");
    }
}

// Polymorphism in action
List<Shape> shapes = new List<Shape>
{
    new Circle(),
    new Square(),
    new Circle()
};

foreach (Shape shape in shapes)
{
    shape.Draw();  // Calls appropriate Draw method
}

SOLID Principles

S - Single Responsibility Principle:

Each class should have only one reason to change.

// ❌ Bad - Multiple responsibilities
public class User
{
    public void Register() { }
    public void SendEmail() { }
    public void SaveToDatabase() { }
}

// ✅ Good - Single responsibility per class
public class User
{
    public string Name { get; set; }
    public string Email { get; set; }
}

public class UserRegistration
{
    public void Register(User user) { }
}

public class EmailService
{
    public void SendEmail(string to, string subject, string body) { }
}

public class UserRepository
{
    public void Save(User user) { }
}

O - Open/Closed Principle:

Open for extension, closed for modification.

// ❌ Bad - Need to modify when adding new shapes
public class AreaCalculator
{
    public double CalculateArea(object shape)
    {
        if (shape is Circle circle)
            return Math.PI * circle.Radius * circle.Radius;
        else if (shape is Rectangle rect)
            return rect.Width * rect.Height;
        // Need to add more if-else for new shapes
        return 0;
    }
}

// ✅ Good - Can extend without modifying
public abstract class Shape
{
    public abstract double CalculateArea();
}

public class Circle : Shape
{
    public double Radius { get; set; }
    public override double CalculateArea() 
        => Math.PI * Radius * Radius;
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    public override double CalculateArea() 
        => Width * Height;
}

L - Liskov Substitution Principle:

Derived classes must be substitutable for their base classes.

// ✅ Good - Square can substitute Rectangle
public class Rectangle
{
    public virtual double Width { get; set; }
    public virtual double Height { get; set; }
    
    public double CalculateArea() => Width * Height;
}

public class Square : Rectangle
{
    private double _side;
    
    public override double Width 
    { 
        get => _side; 
        set => _side = value; 
    }
    
    public override double Height 
    { 
        get => _side; 
        set => _side = value; 
    }
}

I - Interface Segregation Principle:

Don't force clients to depend on interfaces they don't use.

// ❌ Bad - Fat interface
public interface IWorker
{
    void Work();
    void Eat();
    void Sleep();
}

// ✅ Good - Segregated interfaces
public interface IWorkable
{
    void Work();
}

public interface IFeedable
{
    void Eat();
}

public interface ISleepable
{
    void Sleep();
}

public class Human : IWorkable, IFeedable, ISleepable
{
    public void Work() { }
    public void Eat() { }
    public void Sleep() { }
}

public class Robot : IWorkable
{
    public void Work() { }
    // No need to implement Eat and Sleep
}

D - Dependency Inversion Principle:

Depend on abstractions, not concretions.

// ❌ Bad - High-level module depends on low-level module
public class EmailService
{
    public void SendEmail(string message) { }
}

public class NotificationService
{
    private EmailService _emailService = new EmailService();
    
    public void Notify(string message)
    {
        _emailService.SendEmail(message);
    }
}

// ✅ Good - Both depend on abstraction
public interface IMessageSender
{
    void Send(string message);
}

public class EmailSender : IMessageSender
{
    public void Send(string message) { }
}

public class SmsSender : IMessageSender
{
    public void Send(string message) { }
}

public class NotificationService
{
    private readonly IMessageSender _messageSender;
    
    public NotificationService(IMessageSender messageSender)
    {
        _messageSender = messageSender;
    }
    
    public void Notify(string message)
    {
        _messageSender.Send(message);
    }
}

Practice Tasks

  • ✅ Review previous code and identify SOLID violations

  • ✅ Refactor a messy class to follow SRP

  • ✅ Design a plugin system following OCP

  • ✅ Create a notification system following DIP

Resources


🚀 LEVEL 4 – Advanced C# Features

🎯 Step 4.1 – Delegates, Events & Lambda Expressions

Delegates

// Define delegate type
public delegate void MessageDelegate(string message);

public class Notification
{
    public void SendEmail(string message)
    {
        Console.WriteLine($"Email: {message}");
    }
    
    public void SendSMS(string message)
    {
        Console.WriteLine($"SMS: {message}");
    }
}

// Usage
Notification notif = new Notification();
MessageDelegate del = notif.SendEmail;
del("Hello!");  // Email: Hello!

// Multicast delegate
del += notif.SendSMS;
del("Hello!");  // Both Email and SMS sent

Func and Action

// Func - has return value
Func<int, int, int> add = (a, b) => a + b;
int result = add(5, 3);  // 8

Func<string, bool> isLong = (s) => s.Length > 10;

// Action - no return value
Action<string> print = (msg) => Console.WriteLine(msg);
print("Hello!");

Action<int, int> printSum = (a, b) => Console.WriteLine(a + b);

Lambda Expressions

// Single parameter
Func<int, int> square = x => x * x;

// Multiple parameters
Func<int, int, int> multiply = (x, y) => x * y;

// No parameters
Func<int> getRandomNumber = () => new Random().Next();

// Statement lambda
Action<int> printInfo = x =>
{
    Console.WriteLine($"Number: {x}");
    Console.WriteLine($"Square: {x * x}");
};

// Used with List methods
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var evens = numbers.Where(n => n % 2 == 0).ToList();

Events

// Publisher
public class VideoEncoder
{
    // Define delegate
    public delegate void VideoEncodedEventHandler(object sender, EventArgs e);
    
    // Define event based on delegate
    public event VideoEncodedEventHandler VideoEncoded;
    
    public void Encode(string title)
    {
        Console.WriteLine($"Encoding {title}...");
        Thread.Sleep(3000);
        
        // Raise event
        OnVideoEncoded();
    }
    
    protected virtual void OnVideoEncoded()
    {
        VideoEncoded?.Invoke(this, EventArgs.Empty);
    }
}

// Subscriber
public class MailService
{
    public void OnVideoEncoded(object sender, EventArgs e)
    {
        Console.WriteLine("Sending email notification...");
    }
}

// Usage
var encoder = new VideoEncoder();
var mailService = new MailService();

// Subscribe to event
encoder.VideoEncoded += mailService.OnVideoEncoded;
encoder.Encode("Video 1");

Modern Event Pattern with EventHandler<T>:

// Custom event args
public class VideoEventArgs : EventArgs
{
    public string Title { get; set; }
}

public class VideoEncoder
{
    // Using built-in EventHandler<T>
    public event EventHandler<VideoEventArgs> VideoEncoded;
    
    public void Encode(string title)
    {
        Console.WriteLine($"Encoding {title}...");
        OnVideoEncoded(new VideoEventArgs { Title = title });
    }
    
    protected virtual void OnVideoEncoded(VideoEventArgs e)
    {
        VideoEncoded?.Invoke(this, e);
    }
}

Practice Projects

  • ✅ Event-driven notification system

  • ✅ Custom calculator using delegates

  • ✅ Progress reporting system with events

Resources


🔍 Step 4.2 – LINQ (Language Integrated Query)

LINQ Basics

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// Method syntax (preferred)
var evens = numbers.Where(n => n % 2 == 0);
var squares = [numbers.Select](<http://numbers.Select>)(n => n * n);
var first5 = numbers.Take(5);
var skip5 = numbers.Skip(5);

// Query syntax
var evens2 = from n in numbers
             where n % 2 == 0
             select n;

Common LINQ Methods

Filtering:

var result = numbers.Where(n => n > 5);
var first = numbers.First(n => n > 5);  // Throws if not found
var firstOrDefault = numbers.FirstOrDefault(n => n > 100);  // Returns default(T) if not found
var single = numbers.Single(n => n == 5);  // Throws if 0 or >1 results

Projection:

var doubled = [numbers.Select](<http://numbers.Select>)(n => n * 2);
var objects = [numbers.Select](<http://numbers.Select>)(n => new { Number = n, Square = n * n });

// SelectMany - flattens nested collections
List<List<int>> nested = new List<List<int>>
{
    new List<int> { 1, 2 },
    new List<int> { 3, 4, 5 }
};
var flattened = nested.SelectMany(list => list);  // { 1, 2, 3, 4, 5 }

Ordering:

var ascending = numbers.OrderBy(n => n);
var descending = numbers.OrderByDescending(n => n);

// Multiple ordering
var students = GetStudents();
var sorted = students
    .OrderBy(s => s.Grade)
    .ThenByDescending(s => s.Age);

Grouping:

var grouped = students.GroupBy(s => s.Grade);
foreach (var group in grouped)
{
    Console.WriteLine($"Grade {group.Key}:");
    foreach (var student in group)
    {
        Console.WriteLine($"  {[student.Name](<http://student.Name>)}");
    }
}

Aggregation:

int sum = numbers.Sum();
double average = numbers.Average();
int max = numbers.Max();
int min = numbers.Min();
int count = numbers.Count();
bool any = numbers.Any(n => n > 5);
bool all = numbers.All(n => n > 0);

// Custom aggregation
int product = numbers.Aggregate((acc, n) => acc * n);

Joining:

var students = GetStudents();
var courses = GetCourses();

// Inner join
var enrolled = students.Join(
    courses,
    student => student.CourseId,
    course => [course.Id](<http://course.Id>),
    (student, course) => new 
    { 
        StudentName = [student.Name](<http://student.Name>),
        CourseName = course.Title
    });

// Group join
var grouped = courses.GroupJoin(
    students,
    course => [course.Id](<http://course.Id>),
    student => student.CourseId,
    (course, studentGroup) => new
    {
        Course = course.Title,
        Students = studentGroup
    });

Set Operations:

var list1 = new[] { 1, 2, 3, 4 };
var list2 = new[] { 3, 4, 5, 6 };

var distinct = list1.Concat(list2).Distinct();  // { 1, 2, 3, 4, 5, 6 }
var union = list1.Union(list2);                 // { 1, 2, 3, 4, 5, 6 }
var intersect = list1.Intersect(list2);         // { 3, 4 }
var except = list1.Except(list2);               // { 1, 2 }

Partitioning:

var first3 = numbers.Take(3);
var last3 = numbers.TakeLast(3);
var skip3 = numbers.Skip(3);
var takeWhile = numbers.TakeWhile(n => n < 5);
var skipWhile = numbers.SkipWhile(n => n < 5);

Complex LINQ Example

public class Product
{
    public string Name { get; set; }
    public string Category { get; set; }
    public decimal Price { get; set; }
    public int Stock { get; set; }
}

var products = GetProducts();

// Complex query
var result = products
    .Where(p => p.Stock > 0)
    .Where(p => p.Price < 100)
    .GroupBy(p => p.Category)
    .Select(g => new
    {
        Category = g.Key,
        Count = g.Count(),
        AveragePrice = g.Average(p => p.Price),
        Products = g.OrderBy(p => p.Price).ToList()
    })
    .OrderByDescending(x => x.Count);

foreach (var category in result)
{
    Console.WriteLine($"{category.Category}: {category.Count} products, " +
                      $"avg price: ${category.AveragePrice:F2}");
}

Practice Projects

  • ✅ Product filtering and reporting system

  • ✅ Student grade analyzer with LINQ

  • ✅ Log file analyzer using LINQ

  • ✅ Data transformation pipelines

Resources


⚠️ Step 4.3 – Exception Handling & Debugging

Try-Catch-Finally

try
{
    // Code that might throw exception
    int result = 10 / 0;
}
catch (DivideByZeroException ex)
{
    // Handle specific exception
    Console.WriteLine($"Error: {ex.Message}");
}
catch (Exception ex)
{
    // Handle any other exception
    Console.WriteLine($"Unexpected error: {ex.Message}");
    throw;  // Re-throw to preserve stack trace
}
finally
{
    // Always executes (cleanup code)
    Console.WriteLine("Cleanup completed");
}

Common Exception Types

// ArgumentException - invalid argument
public void SetAge(int age)
{
    if (age < 0 || age > 150)
        throw new ArgumentException("Age must be between 0 and 150", nameof(age));
}

// ArgumentNullException - null argument
public void ProcessName(string name)
{
    if (name == null)
        throw new ArgumentNullException(nameof(name));
}

// InvalidOperationException - invalid state
public void Withdraw(decimal amount)
{
    if (_balance < amount)
        throw new InvalidOperationException("Insufficient funds");
}

// NotImplementedException - placeholder
public void FutureFeature()
{
    throw new NotImplementedException("This feature is coming soon");
}

Custom Exceptions

// Define custom exception
public class InsufficientFundsException : Exception
{
    public decimal Balance { get; }
    public decimal RequestedAmount { get; }
    
    public InsufficientFundsException(decimal balance, decimal requestedAmount)
        : base($"Insufficient funds. Balance: {balance}, Requested: {requestedAmount}")
    {
        Balance = balance;
        RequestedAmount = requestedAmount;
    }
}

// Usage
public void Withdraw(decimal amount)
{
    if (_balance < amount)
        throw new InsufficientFundsException(_balance, amount);
    
    _balance -= amount;
}

// Catch custom exception
try
{
    account.Withdraw(1000);
}
catch (InsufficientFundsException ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine($"Shortfall: {ex.RequestedAmount - ex.Balance}");
}

Exception Best Practices

// ✅ Good - Specific exceptions
try
{
    var data = File.ReadAllText("config.json");
}
catch (FileNotFoundException ex)
{
    Console.WriteLine("Config file not found");
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("Permission denied");
}

// ❌ Bad - Catching all exceptions
try
{
    // Code
}
catch (Exception)
{
    // Hiding all errors
}

// ✅ Good - Re-throwing with throw;
catch (Exception ex)
{
    LogError(ex);
    throw;  // Preserves stack trace
}

// ❌ Bad - Re-throwing with throw ex;
catch (Exception ex)
{
    LogError(ex);
    throw ex;  // Loses original stack trace
}

Exception Filters (C# 6+)

try
{
    ProcessData();
}
catch (HttpException ex) when (ex.StatusCode == 404)
{
    Console.WriteLine("Resource not found");
}
catch (HttpException ex) when (ex.StatusCode == 500)
{
    Console.WriteLine("Server error");
}

Debugging in Visual Studio

Breakpoints:

  • Click in left margin to set breakpoint

  • F9 to toggle breakpoint

  • Conditional breakpoints (right-click breakpoint)

Debug Controls:

  • F5 - Start debugging

  • F10 - Step over (execute current line)

  • F11 - Step into (enter method)

  • Shift+F11 - Step out (exit method)

  • F5 - Continue to next breakpoint

Watch & Inspect:

  • Hover over variables to see values

  • Watch window - monitor specific variables

  • Locals window - see all local variables

  • Call stack - see method call hierarchy

  • Immediate window - execute code during debugging

Debug Tips:

// Use Debug class for output
Debug.WriteLine("Debug message");
Debug.Assert(value > 0, "Value must be positive");

// Conditional compilation
#if DEBUG
    Console.WriteLine("Debug mode");
#endif

// Debugger attributes
[DebuggerDisplay("Name = {Name}, Age = {Age}")]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Practice Tasks

  • ✅ Add exception handling to all file operations

  • ✅ Create custom exceptions for business logic

  • ✅ Practice debugging with breakpoints and watches

  • ✅ Use try-catch in database operations

Resources


📁 Step 4.4 – File & Streams

File Operations

using [System.IO](<http://System.IO>);

// Check if file exists
bool exists = File.Exists("data.txt");

// Read entire file
string content = File.ReadAllText("data.txt");
string[] lines = File.ReadAllLines("data.txt");
byte[] bytes = File.ReadAllBytes("data.bin");

// Write to file
File.WriteAllText("output.txt", "Hello World");
File.WriteAllLines("output.txt", new[] { "Line 1", "Line 2" });
File.WriteAllBytes("output.bin", bytes);

// Append to file
File.AppendAllText("log.txt", "New log entry\\n");
File.AppendAllLines("log.txt", new[] { "Entry 1", "Entry 2" });

// Copy, move, delete
File.Copy("source.txt", "destination.txt");
File.Move("old.txt", "new.txt");
File.Delete("temp.txt");

// Get file info
FileInfo fileInfo = new FileInfo("data.txt");
long size = fileInfo.Length;
DateTime created = fileInfo.CreationTime;
DateTime modified = fileInfo.LastWriteTime;
bool isReadOnly = fileInfo.IsReadOnly;

Directory Operations

// Check if directory exists
bool exists = Directory.Exists("MyFolder");

// Create directory
Directory.CreateDirectory("MyFolder/SubFolder");

// Get files and directories
string[] files = Directory.GetFiles("MyFolder");
string[] directories = Directory.GetDirectories("MyFolder");
string[] allFiles = Directory.GetFiles("MyFolder", "*.*", SearchOption.AllDirectories);

// Search patterns
string[] txtFiles = Directory.GetFiles("MyFolder", "*.txt");
string[] csvFiles = Directory.GetFiles("MyFolder", "*.csv");

// Move and delete
Directory.Move("OldFolder", "NewFolder");
Directory.Delete("TempFolder", recursive: true);

// Get directory info
DirectoryInfo dirInfo = new DirectoryInfo("MyFolder");
FileInfo[] files = dirInfo.GetFiles();
DirectoryInfo[] subdirs = dirInfo.GetDirectories();

Streams - StreamReader & StreamWriter

// Write with StreamWriter
using (StreamWriter writer = new StreamWriter("output.txt"))
{
    writer.WriteLine("Line 1");
    writer.WriteLine("Line 2");
    writer.Write("No newline");
}

// Read with StreamReader
using (StreamReader reader = new StreamReader("input.txt"))
{
    string line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}

// Or read all
using (StreamReader reader = new StreamReader("input.txt"))
{
    string content = reader.ReadToEnd();
    Console.WriteLine(content);
}

FileStream

// Write binary data
using (FileStream fs = new FileStream("data.bin", FileMode.Create))
{
    byte[] data = { 1, 2, 3, 4, 5 };
    fs.Write(data, 0, data.Length);
}

// Read binary data
using (FileStream fs = new FileStream("data.bin", [FileMode.Open](<http://FileMode.Open>)))
{
    byte[] buffer = new byte[fs.Length];
    [fs.Read](<http://fs.Read>)(buffer, 0, buffer.Length);
}

// FileMode options:
// - Create: Create new (overwrites existing)
// - CreateNew: Create new (fails if exists)
// - Open: Open existing (fails if not exists)
// - OpenOrCreate: Open or create
// - Append: Open and seek to end
// - Truncate: Open and clear content

Using Statement & IDisposable

// Traditional using
using (var reader = new StreamReader("file.txt"))
{
    // Use reader
}  // Automatically disposes

// C# 8+ using declaration
using var reader = new StreamReader("file.txt");
// Use reader
// Automatically disposes at end of scope

// Multiple resources
using (var reader = new StreamReader("input.txt"))
using (var writer = new StreamWriter("output.txt"))
{
    string line;
    while ((line = reader.ReadLine()) != null)
    {
        writer.WriteLine(line.ToUpper());
    }
}

Path Helper

// Combine paths safely
string path = Path.Combine("folder", "subfolder", "file.txt");

// Get parts of path
string directory = Path.GetDirectoryName(path);
string filename = Path.GetFileName(path);
string filenameNoExt = Path.GetFileNameWithoutExtension(path);
string extension = Path.GetExtension(path);

// Temporary files
string tempPath = Path.GetTempPath();
string tempFile = Path.GetTempFileName();

// Special folders
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

Working with JSON

using System.Text.Json;

// Object to serialize
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// Serialize to JSON
var person = new Person { Name = "John", Age = 30 };
string json = JsonSerializer.Serialize(person);
File.WriteAllText("person.json", json);

// Serialize with formatting
var options = new JsonSerializerOptions { WriteIndented = true };
string jsonFormatted = JsonSerializer.Serialize(person, options);

// Deserialize from JSON
string json = File.ReadAllText("person.json");
Person person = JsonSerializer.Deserialize<Person>(json);

Practice Projects

  • ✅ Note-taking console app (save/load notes from files)

  • ✅ Log file analyzer

  • ✅ File backup utility

  • ✅ Configuration manager (JSON settings)

  • ✅ CSV reader/writer

Resources


⚡ Step 4.5 – Async Programming & Multithreading

Why Async?

  • Keep UI responsive

  • Efficient I/O operations (file, network, database)

  • Better resource utilization

  • Don't block threads while waiting

Async/Await Basics

// Async method returns Task or Task<T>
public async Task<string> DownloadDataAsync(string url)
{
    using (HttpClient client = new HttpClient())
    {
        // await suspends execution until operation completes
        string data = await client.GetStringAsync(url);
        return data;
    }
}

// Calling async method
public async Task ProcessDataAsync()
{
    string data = await DownloadDataAsync("<https://api.example.com>");
    Console.WriteLine(data);
}

// Async void - only for event handlers
private async void Button_Click(object sender, EventArgs e)
{
    await ProcessDataAsync();
}

Task Parallel Library

// Create and run tasks
Task task1 = [Task.Run](<http://Task.Run>)(() => 
{
    Console.WriteLine("Task 1");
});

Task<int> task2 = [Task.Run](<http://Task.Run>)(() => 
{
    return 42;
});

// Wait for task
task1.Wait();
int result = task2.Result;  // Blocks until complete

// Async file operations
string content = await File.ReadAllTextAsync("file.txt");
await File.WriteAllTextAsync("file.txt", "content");

// Delay
await Task.Delay(1000);  // Wait 1 second (non-blocking)

Multiple Async Operations

// Run tasks in parallel
List<string> urls = GetUrls();
List<Task<string>> tasks = new List<Task<string>>();

foreach (string url in urls)
{
    tasks.Add(DownloadDataAsync(url));
}

// Wait for all
string[] results = await Task.WhenAll(tasks);

// Wait for any
Task<string> completedTask = await Task.WhenAny(tasks);
string result = await completedTask;

// Parallel processing with results
var results = await Task.WhenAll(
    DownloadAsync("url1"),
    DownloadAsync("url2"),
    DownloadAsync("url3")
);

ConfigureAwait

// In library code, use ConfigureAwait(false)
public async Task<string> GetDataAsync()
{
    using (HttpClient client = new HttpClient())
    {
        // Don't capture synchronization context
        string data = await client.GetStringAsync(url)
            .ConfigureAwait(false);
        return data;
    }
}

// In UI code, don't use ConfigureAwait (or use ConfigureAwait(true))
private async void Button_Click(object sender, EventArgs e)
{
    string data = await GetDataAsync();
    textBox.Text = data;  // Must run on UI thread
}

Threading Basics

// Create thread (old way)
Thread thread = new Thread(() => 
{
    Console.WriteLine("Thread running");
});
thread.Start();
thread.Join();  // Wait for completion

// Thread pool (better)
ThreadPool.QueueUserWorkItem(state => 
{
    Console.WriteLine("Thread pool work");
});

// Task (best for most scenarios)
await [Task.Run](<http://Task.Run>)(() => 
{
    // CPU-intensive work
    ComputeSomething();
});

Cancellation

public async Task DownloadWithCancellationAsync(string url, 
    CancellationToken cancellationToken)
{
    using (HttpClient client = new HttpClient())
    {
        string data = await client.GetStringAsync(url, cancellationToken);
        
        // Check for cancellation
        cancellationToken.ThrowIfCancellationRequeste
d();
        
        return data;
    }
}

// Usage
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(5));  // Cancel after 5 seconds

try
{
    string data = await DownloadWithCancellationAsync(url, cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation was cancelled");
}

Practice Projects

  • ✅ URL downloader with multiple parallel downloads

  • ✅ File processor with async I/O

  • ✅ Web scraper with cancellation support

Resources


🔬 Step 4.6 – Reflection, Indexers & Extensions

Indexers

public class WeeklySchedule
{
    private string[] days = new string[7];
    
    // Indexer
    public string this[int index]
    {
        get { return days[index]; }
        set { days[index] = value; }
    }
    
    // String indexer
    public string this[string dayName]
    {
        get
        {
            return dayName switch
            {
                "Monday" => days[0],
                "Tuesday" => days[1],
                _ => throw new ArgumentException("Invalid day")
            };
        }
        set
        {
            switch (dayName)
            {
                case "Monday": days[0] = value; break;
                case "Tuesday": days[1] = value; break;
                // ... etc
            }
        }
    }
}

// Usage
var schedule = new WeeklySchedule();
schedule[0] = "Meeting at 9 AM";
schedule["Monday"] = "Meeting at 9 AM";

Reflection

using System.Reflection;

// Get type information
Type type = typeof(Person);
// Or from instance
Person person = new Person();
Type type2 = person.GetType();

// Get properties
PropertyInfo[] properties = type.GetProperties();
foreach (var prop in properties)
{
    Console.WriteLine($"{[prop.Name](<http://prop.Name>)}: {prop.PropertyType}");
}

// Get and set property values
object instance = Activator.CreateInstance(type);
PropertyInfo nameProp = type.GetProperty("Name");
nameProp.SetValue(instance, "John");
object value = nameProp.GetValue(instance);

// Get methods
MethodInfo[] methods = type.GetMethods();
MethodInfo method = type.GetMethod("MethodName");
object result = method.Invoke(instance, new object[] { param1, param2 });

// Get custom attributes
var attributes = type.GetCustomAttributes();

Extension Methods

// Must be in static class
public static class StringExtensions
{
    // First parameter with 'this' keyword
    public static bool IsValidEmail(this string email)
    {
        return email.Contains("@") && email.Contains(".");
    }
    
    public static string Truncate(this string str, int maxLength)
    {
        if (string.IsNullOrEmpty(str) || str.Length <= maxLength)
            return str;
        
        return str.Substring(0, maxLength) + "...";
    }
    
    public static int WordCount(this string str)
    {
        return str.Split(new[] { ' ', '\\t', '\\n' }, 
            StringSplitOptions.RemoveEmptyEntries).Length;
    }
}

// Usage
string email = "[user@example.com](<mailto:user@example.com>)";
bool valid = email.IsValidEmail();  // Called as if it's a method of string

string text = "This is a very long text";
string truncated = text.Truncate(10);  // "This is a ..."

Expression Trees (Basic)

using System.Linq.Expressions;

// Create expression: x => x * x
ParameterExpression param = Expression.Parameter(typeof(int), "x");
BinaryExpression multiply = Expression.Multiply(param, param);
Expression<Func<int, int>> lambda = Expression.Lambda<Func<int, int>>(multiply, param);

// Compile and execute
Func<int, int> square = lambda.Compile();
int result = square(5);  // 25

Practice Projects

  • ✅ Utility extension methods for common types

  • ✅ Simple plugin loader using reflection

  • ✅ Object mapper using reflection

Resources


🗄️ LEVEL 5 – Data Access & Databases

🗃️ Step 5.1 – SQL Server Basics

Install SQL Server

Options:

  • SQL Server Express (free, full-featured)

  • SQL Server LocalDB (lightweight, developer-focused)

  • SQL Server Developer Edition (free for development)

Management Tools:

  • SQL Server Management Studio (SSMS)

  • Azure Data Studio (cross-platform)

Database Fundamentals

Key Concepts:

  • Database: Container for tables and other objects

  • Table: Stores data in rows and columns

  • Primary Key: Unique identifier for each row

  • Foreign Key: References primary key in another table

  • Index: Improves query performance

Basic SQL Commands

CREATE TABLE:

CREATE TABLE Students (
    StudentId INT PRIMARY KEY IDENTITY(1,1),
    FirstName NVARCHAR(50) NOT NULL,
    LastName NVARCHAR(50) NOT NULL,
    Email NVARCHAR(100) UNIQUE,
    BirthDate DATE,
    EnrollmentDate DATETIME DEFAULT GETDATE()
);

CREATE TABLE Courses (
    CourseId INT PRIMARY KEY IDENTITY(1,1),
    CourseName NVARCHAR(100) NOT NULL,
    Credits INT
);

CREATE TABLE Enrollments (
    EnrollmentId INT PRIMARY KEY IDENTITY(1,1),
    StudentId INT FOREIGN KEY REFERENCES Students(StudentId),
    CourseId INT FOREIGN KEY REFERENCES Courses(CourseId),
    Grade DECIMAL(3,2)
);

INSERT:

INSERT INTO Students (FirstName, LastName, Email, BirthDate)
VALUES ('John', 'Doe', '[john@example.com](<mailto:john@example.com>)', '2000-01-15');

-- Multiple rows
INSERT INTO Students (FirstName, LastName, Email)
VALUES 
    ('Alice', 'Smith', '[alice@example.com](<mailto:alice@example.com>)'),
    ('Bob', 'Johnson', '[bob@example.com](<mailto:bob@example.com>)');

SELECT:

-- Select all
SELECT * FROM Students;

-- Select specific columns
SELECT FirstName, LastName, Email FROM Students;

-- With WHERE clause
SELECT * FROM Students WHERE EnrollmentDate > '2024-01-01';

-- With ORDER BY
SELECT * FROM Students ORDER BY LastName, FirstName;

-- Top N
SELECT TOP 10 * FROM Students ORDER BY EnrollmentDate DESC;

UPDATE:

UPDATE Students
SET Email = '[newemail@example.com](<mailto:newemail@example.com>)'
WHERE StudentId = 1;

-- Update multiple columns
UPDATE Students
SET FirstName = 'Jonathan', LastName = 'Doe'
WHERE StudentId = 1;

DELETE:

DELETE FROM Students WHERE StudentId = 5;

-- Delete all (careful!)
DELETE FROM Students;

JOINS:

-- Inner join
SELECT s.FirstName, s.LastName, c.CourseName, e.Grade
FROM Students s
INNER JOIN Enrollments e ON s.StudentId = e.StudentId
INNER JOIN Courses c ON e.CourseId = c.CourseId;

-- Left join
SELECT s.FirstName, s.LastName, c.CourseName
FROM Students s
LEFT JOIN Enrollments e ON s.StudentId = e.StudentId
LEFT JOIN Courses c ON e.CourseId = c.CourseId;

Aggregate Functions:

SELECT COUNT(*) FROM Students;
SELECT AVG(Grade) FROM Enrollments;
SELECT MAX(Grade), MIN(Grade) FROM Enrollments;

-- Group by
SELECT CourseId, AVG(Grade) as AverageGrade
FROM Enrollments
GROUP BY CourseId
HAVING AVG(Grade) > 75;

Practice Tasks

  • ✅ Design a simple database schema (Students, Courses, Enrollments)

  • ✅ Write CRUD queries for all tables

  • ✅ Practice joins and aggregate queries


🔌 Step 5.2 – ADO.NET

Connection String

// SQL Server connection string
string connectionString = 
    "Server=[localhost](<http://localhost>);Database=SchoolDB;Integrated Security=true;";
// Or with username/password
string connectionString2 = 
    "Server=[localhost](<http://localhost>);Database=SchoolDB;User Id=sa;Password=yourpassword;";

Basic ADO.NET Operations

Opening Connection:

using [System.Data](<http://System.Data>).SqlClient;

using (SqlConnection connection = new SqlConnection(connectionString))
{
    [connection.Open](<http://connection.Open>)();
    Console.WriteLine("Connected!");
    // Work with database
}  // Connection automatically closed

Execute Non-Query (INSERT, UPDATE, DELETE):

using (SqlConnection connection = new SqlConnection(connectionString))
{
    [connection.Open](<http://connection.Open>)();
    
    string sql = "INSERT INTO Students (FirstName, LastName, Email) " +
                 "VALUES (@FirstName, @LastName, @Email)";
    
    using (SqlCommand command = new SqlCommand(sql, connection))
    {
        // Use parameters to prevent SQL injection
        command.Parameters.AddWithValue("@FirstName", "John");
        command.Parameters.AddWithValue("@LastName", "Doe");
        command.Parameters.AddWithValue("@Email", "[john@example.com](<mailto:john@example.com>)");
        
        int rowsAffected = command.ExecuteNonQuery();
        Console.WriteLine($"{rowsAffected} row(s) inserted");
    }
}

Execute Scalar (Get Single Value):

using (SqlConnection connection = new SqlConnection(connectionString))
{
    [connection.Open](<http://connection.Open>)();
    
    string sql = "SELECT COUNT(*) FROM Students";
    
    using (SqlCommand command = new SqlCommand(sql, connection))
    {
        int count = (int)command.ExecuteScalar();
        Console.WriteLine($"Total students: {count}");
    }
}

Execute Reader (Get Multiple Rows):

using (SqlConnection connection = new SqlConnection(connectionString))
{
    [connection.Open](<http://connection.Open>)();
    
    string sql = "SELECT StudentId, FirstName, LastName, Email FROM Students";
    
    using (SqlCommand command = new SqlCommand(sql, connection))
    using (SqlDataReader reader = command.ExecuteReader())
    {
        while ([reader.Read](<http://reader.Read>)())
        {
            int id = reader.GetInt32(0);
            string firstName = reader.GetString(1);
            string lastName = reader.GetString(2);
            string email = reader.GetString(3);
            
            Console.WriteLine($"{id}: {firstName} {lastName} ({email})");
        }
    }
}

Safe Parameter Usage:

// ❌ Bad - SQL Injection vulnerable
string sql = $"SELECT * FROM Users WHERE Username = '{username}'";

// ✅ Good - Parameterized query
string sql = "SELECT * FROM Users WHERE Username = @Username";
command.Parameters.AddWithValue("@Username", username);

Transactions:

using (SqlConnection connection = new SqlConnection(connectionString))
{
    [connection.Open](<http://connection.Open>)();
    SqlTransaction transaction = connection.BeginTransaction();
    
    try
    {
        // First operation
        string sql1 = "INSERT INTO Accounts (Name, Balance) VALUES (@Name, @Balance)";
        using (SqlCommand cmd = new SqlCommand(sql1, connection, transaction))
        {
            cmd.Parameters.AddWithValue("@Name", "Account1");
            cmd.Parameters.AddWithValue("@Balance", 1000);
            cmd.ExecuteNonQuery();
        }
        
        // Second operation
        string sql2 = "UPDATE Accounts SET Balance = Balance - 100 WHERE Name = 'Account2'";
        using (SqlCommand cmd = new SqlCommand(sql2, connection, transaction))
        {
            cmd.ExecuteNonQuery();
        }
        
        // Commit if all successful
        transaction.Commit();
        Console.WriteLine("Transaction completed");
    }
    catch (Exception ex)
    {
        // Rollback on error
        transaction.Rollback();
        Console.WriteLine($"Transaction failed: {ex.Message}");
    }
}

Practice Projects

  • ✅ CRUD console app for Students using ADO.NET

  • ✅ Simple inventory management system

  • ✅ Banking transaction simulator with proper transactions

Resources


🚀 Step 5.3 – Entity Framework Core

What is EF Core?

Entity Framework Core is an Object-Relational Mapper (ORM) that:

  • Maps C# classes to database tables

  • Generates SQL queries automatically

  • Tracks changes to objects

  • Handles relationships between entities

Installation

dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package [Microsoft.EntityFrameworkCore.Tools](<http://Microsoft.EntityFrameworkCore.Tools>)

Define Entity Classes

public class Student
{
    public int StudentId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public DateTime EnrollmentDate { get; set; }
    
    // Navigation property
    public List<Enrollment> Enrollments { get; set; }
}

public class Course
{
    public int CourseId { get; set; }
    public string CourseName { get; set; }
    public int Credits { get; set; }
    
    public List<Enrollment> Enrollments { get; set; }
}

public class Enrollment
{
    public int EnrollmentId { get; set; }
    public int StudentId { get; set; }
    public int CourseId { get; set; }
    public decimal? Grade { get; set; }
    
    // Navigation properties
    public Student Student { get; set; }
    public Course Course { get; set; }
}

DbContext

using Microsoft.EntityFrameworkCore;

public class SchoolContext : DbContext
{
    public DbSet<Student> Students { get; set; }
    public DbSet<Course> Courses { get; set; }
    public DbSet<Enrollment> Enrollments { get; set; }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(
            "Server=[localhost](<http://localhost>);Database=SchoolDB;Integrated Security=true;");
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Fluent API configuration
        modelBuilder.Entity<Student>()
            .Property(s => [s.Email](<http://s.Email>))
            .IsRequired()
            .HasMaxLength(100);
        
        modelBuilder.Entity<Enrollment>()
            .HasOne(e => e.Student)
            .WithMany(s => s.Enrollments)
            .HasForeignKey(e => e.StudentId);
        
        modelBuilder.Entity<Enrollment>()
            .HasOne(e => e.Course)
            .WithMany(c => c.Enrollments)
            .HasForeignKey(e => e.CourseId);
    }
}

Data Annotations

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

public class Student
{
    [Key]
    public int StudentId { get; set; }
    
    [Required]
    [MaxLength(50)]
    public string FirstName { get; set; }
    
    [Required]
    [MaxLength(50)]
    public string LastName { get; set; }
    
    [EmailAddress]
    [MaxLength(100)]
    public string Email { get; set; }
    
    [Column(TypeName = "date")]
    public DateTime BirthDate { get; set; }
}

Migrations

# Create initial migration
dotnet ef migrations add InitialCreate

# Apply migration to database
dotnet ef database update

# Add new migration after model changes
dotnet ef migrations add AddPhoneNumber

# Remove last migration (if not applied)
dotnet ef migrations remove

# Generate SQL script
dotnet ef migrations script

CRUD Operations with EF Core

Create:

using (var context = new SchoolContext())
{
    var student = new Student
    {
        FirstName = "John",
        LastName = "Doe",
        Email = "[john@example.com](<mailto:john@example.com>)",
        EnrollmentDate = [DateTime.Now](<http://DateTime.Now>)
    };
    
    context.Students.Add(student);
    context.SaveChanges();
    
    Console.WriteLine($"Student added with ID: {student.StudentId}");
}

Read:

using (var context = new SchoolContext())
{
    // Get all
    var allStudents = context.Students.ToList();
    
    // Get by ID
    var student = context.Students.Find(1);
    
    // Query with LINQ
    var filteredStudents = context.Students
        .Where(s => s.EnrollmentDate > [DateTime.Now](<http://DateTime.Now>).AddYears(-1))
        .OrderBy(s => s.LastName)
        .ToList();
    
    // First or default
    var firstStudent = context.Students
        .FirstOrDefault(s => [s.Email](<http://s.Email>) == "[john@example.com](<mailto:john@example.com>)");
}

Update:

using (var context = new SchoolContext())
{
    var student = context.Students.Find(1);
    if (student != null)
    {
        [student.Email](<http://student.Email>) = "[newemail@example.com](<mailto:newemail@example.com>)";
        context.SaveChanges();
    }
}

Delete:

using (var context = new SchoolContext())
{
    var student = context.Students.Find(1);
    if (student != null)
    {
        context.Students.Remove(student);
        context.SaveChanges();
    }
}

Loading Related Data

Eager Loading:

// Load students with their enrollments
var students = context.Students
    .Include(s => s.Enrollments)
        .ThenInclude(e => e.Course)
    .ToList();

Explicit Loading:

var student = context.Students.Find(1);
context.Entry(student)
    .Collection(s => s.Enrollments)
    .Load();

Lazy Loading:

// Install: Microsoft.EntityFrameworkCore.Proxies
// In OnConfiguring:
optionsBuilder.UseLazyLoadingProxies();

// Make navigation properties virtual
public virtual List<Enrollment> Enrollments { get; set; }

Practice Projects

  • ✅ Student management system with EF Core

  • ✅ Blog platform (Posts, Comments, Users)

  • ✅ E-commerce product catalog

Resources


🎓 LEVEL 6 – Professional Development Skills

✅ Step 6.1 – Unit Testing

Why Unit Testing?

  • Catch bugs early

  • Enable refactoring with confidence

  • Document code behavior

  • Improve code design

Setup xUnit

# Create test project
dotnet new xunit -n MyProject.Tests

# Add reference to main project
dotnet add reference ../MyProject/MyProject.csproj

# Install packages
dotnet add package Moq  # For mocking

Basic Test Structure

using Xunit;

public class CalculatorTests
{
    [Fact]
    public void Add_TwoPositiveNumbers_ReturnsSum()
    {
        // Arrange
        var calculator = new Calculator();
        
        // Act
        int result = calculator.Add(5, 3);
        
        // Assert
        Assert.Equal(8, result);
    }
    
    [Theory]
    [InlineData(2, 3, 5)]
    [InlineData(0, 0, 0)]
    [InlineData(-2, 3, 1)]
    public void Add_VariousInputs_ReturnsCorrectSum(int a, int b, int expected)
    {
        var calculator = new Calculator();
        
        int result = calculator.Add(a, b);
        
        Assert.Equal(expected, result);
    }
}

Common Assertions

// Equality
Assert.Equal(expected, actual);
Assert.NotEqual(expected, actual);

// Boolean
Assert.True(condition);
Assert.False(condition);

// Null checks
Assert.Null(obj);
Assert.NotNull(obj);

// Exceptions
Assert.Throws<ArgumentException>(() => method());

// Collections
Assert.Empty(collection);
Assert.NotEmpty(collection);
Assert.Contains(item, collection);
Assert.DoesNotContain(item, collection);

// Strings
Assert.StartsWith("prefix", text);
Assert.EndsWith("suffix", text);
Assert.Contains("substring", text);

// Ranges
Assert.InRange(actual, low, high);

Mocking with Moq

using Moq;

public interface IEmailService
{
    void SendEmail(string to, string subject, string body);
    bool IsEmailValid(string email);
}

public class UserService
{
    private readonly IEmailService _emailService;
    
    public UserService(IEmailService emailService)
    {
        _emailService = emailService;
    }
    
    public bool RegisterUser(string email)
    {
        if (!_emailService.IsEmailValid(email))
            return false;
        
        _emailService.SendEmail(email, "Welcome", "Thanks for registering");
        return true;
    }
}

// Test with mock
public class UserServiceTests
{
    [Fact]
    public void RegisterUser_ValidEmail_SendsWelcomeEmail()
    {
        // Arrange
        var mockEmailService = new Mock<IEmailService>();
        mockEmailService.Setup(e => e.IsEmailValid(It.IsAny<string>()))
            .Returns(true);
        
        var userService = new UserService(mockEmailService.Object);
        
        // Act
        bool result = userService.RegisterUser("[test@example.com](<mailto:test@example.com>)");
        
        // Assert
        Assert.True(result);
        mockEmailService.Verify(e => 
            e.SendEmail("[test@example.com](<mailto:test@example.com>)", "Welcome", "Thanks for registering"), 
            Times.Once);
    }
}

Test-Driven Development (TDD)

Red-Green-Refactor Cycle:

  1. Red: Write a failing test

  2. Green: Write minimal code to pass

  3. Refactor: Improve code while keeping tests green

Practice Tasks

  • ✅ Write tests for calculator class

  • ✅ Test LINQ queries and data transformations

  • ✅ Mock dependencies in business logic

  • ✅ Achieve >80% code coverage on a project


🏗️ Step 6.2 – Design Patterns

Creational Patterns

Singleton:

public class Logger
{
    private static Logger _instance;
    private static readonly object _lock = new object();
    
    private Logger() { }
    
    public static Logger Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lock)
                {
                    if (_instance == null)
                        _instance = new Logger();
                }
            }
            return _instance;
        }
    }
    
    public void Log(string message)
    {
        Console.WriteLine($"[{[DateTime.Now](<http://DateTime.Now>)}] {message}");
    }
}

// Usage
Logger.Instance.Log("Application started");

Factory:

public interface IPayment
{
    void ProcessPayment(decimal amount);
}

public class CreditCardPayment : IPayment
{
    public void ProcessPayment(decimal amount)
    {
        Console.WriteLine($"Processing ${amount} via Credit Card");
    }
}

public class PayPalPayment : IPayment
{
    public void ProcessPayment(decimal amount)
    {
        Console.WriteLine($"Processing ${amount} via PayPal");
    }
}

public class PaymentFactory
{
    public static IPayment CreatePayment(string type)
    {
        return type.ToLower() switch
        {
            "creditcard" => new CreditCardPayment(),
            "paypal" => new PayPalPayment(),
            _ => throw new ArgumentException("Invalid payment type")
        };
    }
}

// Usage
IPayment payment = PaymentFactory.CreatePayment("creditcard");
payment.ProcessPayment(100);

Builder:

public class Pizza
{
    public string Size { get; set; }
    public bool Cheese { get; set; }
    public bool Pepperoni { get; set; }
    public bool Mushrooms { get; set; }
}

public class PizzaBuilder
{
    private Pizza _pizza = new Pizza();
    
    public PizzaBuilder SetSize(string size)
    {
        _pizza.Size = size;
        return this;
    }
    
    public PizzaBuilder AddCheese()
    {
        _pizza.Cheese = true;
        return this;
    }
    
    public PizzaBuilder AddPepperoni()
    {
        _pizza.Pepperoni = true;
        return this;
    }
    
    public PizzaBuilder AddMushrooms()
    {
        _pizza.Mushrooms = true;
        return this;
    }
    
    public Pizza Build() => _pizza;
}

// Usage
var pizza = new PizzaBuilder()
    .SetSize("Large")
    .AddCheese()
    .AddPepperoni()
    .Build();

Structural Patterns

Adapter:

// Legacy class we can't modify
public class LegacyPrinter
{
    public void PrintOldWay(string text)
    {
        Console.WriteLine($"OLD: {text}");
    }
}

// New interface we want to use
public interface IPrinter
{
    void Print(string document);
}

// Adapter
public class PrinterAdapter : IPrinter
{
    private LegacyPrinter _legacyPrinter;
    
    public PrinterAdapter(LegacyPrinter legacyPrinter)
    {
        _legacyPrinter = legacyPrinter;
    }
    
    public void Print(string document)
    {
        _legacyPrinter.PrintOldWay(document);
    }
}

Decorator:

public interface ICoffee
{
    string GetDescription();
    decimal GetCost();
}

public class SimpleCoffee : ICoffee
{
    public string GetDescription() => "Simple coffee";
    public decimal GetCost() => 2.00m;
}

public abstract class CoffeeDecorator : ICoffee
{
    protected ICoffee _coffee;
    
    public CoffeeDecorator(ICoffee coffee)
    {
        _coffee = coffee;
    }
    
    public virtual string GetDescription() => _coffee.GetDescription();
    public virtual decimal GetCost() => _coffee.GetCost();
}

public class MilkDecorator : CoffeeDecorator
{
    public MilkDecorator(ICoffee coffee) : base(coffee) { }
    
    public override string GetDescription() => _coffee.GetDescription() + ", Milk";
    public override decimal GetCost() => _coffee.GetCost() + 0.50m;
}

// Usage
ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
Console.WriteLine($"{coffee.GetDescription()}: ${coffee.GetCost()}");

Behavioral Patterns

Strategy:

public interface IDiscountStrategy
{
    decimal ApplyDiscount(decimal price);
}

public class NoDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal price) => price;
}

public class PercentageDiscount : IDiscountStrategy
{
    private decimal _percentage;
    
    public PercentageDiscount(decimal percentage)
    {
        _percentage = percentage;
    }
    
    public decimal ApplyDiscount(decimal price)
    {
        return price * (1 - _percentage / 100);
    }
}

public class ShoppingCart
{
    private IDiscountStrategy _discountStrategy;
    
    public ShoppingCart(IDiscountStrategy discountStrategy)
    {
        _discountStrategy = discountStrategy;
    }
    
    public decimal CalculateTotal(decimal price)
    {
        return _discountStrategy.ApplyDiscount(price);
    }
}

Observer:

public interface IObserver
{
    void Update(string message);
}

public interface ISubject
{
    void Attach(IObserver observer);
    void Detach(IObserver observer);
    void Notify(string message);
}

public class NewsAgency : ISubject
{
    private List<IObserver> _observers = new List<IObserver>();
    
    public void Attach(IObserver observer)
    {
        _observers.Add(observer);
    }
    
    public void Detach(IObserver observer)
    {
        _observers.Remove(observer);
    }
    
    public void Notify(string message)
    {
        foreach (var observer in _observers)
        {
            observer.Update(message);
        }
    }
    
    public void PublishNews(string news)
    {
        Console.WriteLine($"Breaking news: {news}");
        Notify(news);
    }
}

public class NewsSubscriber : IObserver
{
    private string _name;
    
    public NewsSubscriber(string name)
    {
        _name = name;
    }
    
    public void Update(string message)
    {
        Console.WriteLine($"{_name} received: {message}");
    }
}

Practice Projects

  • ✅ Implement discount system using Strategy pattern

  • ✅ Create notification system with Observer pattern

  • ✅ Build document export system with Factory pattern


🏛️ Step 6.3 – Clean Architecture & Dependency Injection

Layered Architecture

📁 MyApp.Domain          // Core business logic
   ├── Entities
   ├── Interfaces
   └── Services

📁 MyApp.Application     // Use cases
   ├── DTOs
   ├── Commands
   └── Queries

📁 MyApp.Infrastructure  // External concerns
   ├── Data (EF Core)
   ├── Email
   └── FileStorage

📁 MyApp.API/UI          // Presentation
   ├── Controllers
   └── Views

Dependency Injection in .NET

Configure Services:

using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

// Register services
services.AddTransient<IEmailService, EmailService>();  // New instance each time
services.AddScoped<IRepository, Repository>();         // One per request
services.AddSingleton<ILogger, Logger>();              // One for entire app

// Build service provider
var serviceProvider = services.BuildServiceProvider();

// Resolve service
var emailService = serviceProvider.GetService<IEmailService>();

Constructor Injection:

public class UserService
{
    private readonly IUserRepository _repository;
    private readonly IEmailService _emailService;
    private readonly ILogger _logger;
    
    public UserService(
        IUserRepository repository,
        IEmailService emailService,
        ILogger logger)
    {
        _repository = repository;
        _emailService = emailService;
        _logger = logger;
    }
    
    public async Task RegisterUser(User user)
    {
        await _repository.AddAsync(user);
        await _emailService.SendWelcomeEmail([user.Email](<http://user.Email>));
        _logger.Log($"User {[user.Email](<http://user.Email>)} registered");
    }
}

Clean Code Principles

Meaningful Names:

// ❌ Bad
int d; // elapsed time in days
List<int[]> list1;

// ✅ Good
int elapsedTimeInDays;
List<Account> activeAccounts;

Small Functions:

// ❌ Bad - Function does too much
public void ProcessOrder(Order order)
{
    // Validate
    if (order == null) throw new ArgumentNullException();
    if (order.Items.Count == 0) throw new InvalidOperationException();
    
    // Calculate total
    decimal total = 0;
    foreach (var item in order.Items)
        total += item.Price * item.Quantity;
    
    // Apply discount
    if (order.Customer.IsPremium)
        total *= 0.9m;
    
    // Process payment
    // Send email
    // Update inventory
}

// ✅ Good - Single Responsibility
public void ProcessOrder(Order order)
{
    ValidateOrder(order);
    decimal total = CalculateTotal(order);
    total = ApplyDiscount(total, order.Customer);
    ProcessPayment(order, total);
    SendConfirmationEmail(order);
    UpdateInventory(order);
}

Practice Projects

  • ✅ Refactor existing project into layers

  • ✅ Implement repository pattern with DI

  • ✅ Create clean architecture template


🚀 Step 6.4 – Portfolio Projects

Project 1: Library Management System

Features:

  • Book CRUD operations

  • Member management

  • Book lending/returning

  • Late fee calculation

  • Search and filtering

  • Reports (most borrowed books, overdue items)

Tech Stack:

  • C# Console or Minimal API

  • EF Core with SQL Server

  • LINQ for queries

  • Unit tests

Project 2: Personal Finance Tracker

Features:

  • Income/expense tracking

  • Category management

  • Monthly/yearly reports

  • Budget planning

  • Charts and visualizations

  • Import/export CSV

Tech Stack:

  • C# with EF Core

  • Chart library for visualizations

  • Async file operations

Project 3: Task Management System

Features:

  • Create, edit, delete tasks

  • Priority levels

  • Due dates with reminders

  • Tags/categories

  • Search and filter

  • Mark complete

Tech Stack:

  • C# with modern features

  • SQLite or SQL Server

  • Async/await

  • SOLID principles

For Each Project:

  • ✅ Clean code and architecture

  • ✅ Unit tests (>70% coverage)

  • ✅ GitHub repository with README

  • ✅ Documentation

  • ✅ Use SOLID principles

  • ✅ Proper exception handling


🌟 LEVEL 7 – Master C# 14: The Future of C# Development

🎉 C# 14 ships with .NET 10 (LTS) - Released November 2025, Mastered in 2026

Why C# 14 is Revolutionary: Extension Members, field keyword, and enhanced pattern matching are the biggest language improvements since LINQ. This level focuses intensively on these game-changing features that will dominate C# development in 2026 and beyond.

⚡ Step 7.1 – C# 14 New Features

1. Extension Members (Extension Blocks)

The Headline Feature of C# 14[1]

Extension members dramatically expand what you can extend beyond just methods.

Old Way (C# 1-13):

public static class IntegerExtensions
{
    public static bool IsEven(this int value) => value % 2 == 0;
    public static bool IsOdd(this int value) => value % 2 != 0;
    public static bool IsDefault(this int value) => value == 0;
}

New Way (C# 14) - Extension Blocks:

public static class IntegerExtensions
{
    // Extension block with receiver name
    extension(int value)
    {
        // Extension methods
        public bool IsEven() => value % 2 == 0;
        public bool IsOdd() => value % 2 != 0;
        
        // Extension properties!
        public bool IsDefault => value == 0;
        public bool IsPositive => value > 0;
        public bool IsNegative => value < 0;
        
        // Extension operators!
        public static int operator ++(int x) => x + 1;
        
        // Static extensions
        public static int Parse(string s) => int.Parse(s);
    }
}

// Usage
int number = 10;
bool even = number.IsEven();      // Method
bool positive = number.IsPositive; // Property!

Extension Properties for Collections:

public static class ListExtensions
{
    extension<T>(List<T> list)
    {
        // Extension property
        public bool IsEmpty => list.Count == 0;
        public T FirstItem => list.Count > 0 ? list[0] : default;
        public T LastItem => list.Count > 0 ? list[^1] : default;
        
        // Extension indexer!
        public T this[Index index] => list[index];
    }
}

// Usage
var names = new List<string> { "Alice", "Bob", "Charlie" };
bool empty = names.IsEmpty;          // false
string first = names.FirstItem;      // "Alice"

2. The field Keyword (Semi-Auto Properties)

Simplify property implementations with direct backing field access.[2]

Old Way:

public class Person
{
    private string _name;
    
    public string Name
    {
        get => _name;
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Name cannot be empty");
            _name = value;
        }
    }
}

New Way with field keyword:

public class Person
{
    public string Name
    {
        get => field;  // 'field' refers to compiler-generated backing field
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Name cannot be empty");
            field = value;
        }
    }
    
    // Even more concise
    public int Age
    {
        get => field;
        set => field = value < 0 ? 0 : value;
    }
}

3. Null-Conditional Assignment Operator ??=

Cleaner null-checking before assignment.[[3]](https://dev.to/thecodewrapper/the-3-most-practical-features-of-c-14-for-everyday-developers-3hm7#:~:text=C%23 14 introduces a new,types you don\'t own.)

Old Way:

if (config == null)
{
    config = LoadDefaultConfig();
}

// Or
config = config ?? LoadDefaultConfig();

New Way (C# 14 enhanced):

// Assign only if null
config ??= LoadDefaultConfig();

// Works with properties
user.Settings ??= new Settings();

// Chain multiple
cache ??= database.Get() ??= CreateDefault();

4. Partial Constructors and Events

Split constructor logic across partial classes.[2]

// File1.cs
public partial class UserService
{
    private readonly ILogger _logger;
    
    partial void OnConstructing();
    
    public partial UserService(ILogger logger)
    {
        _logger = logger;
        OnConstructing();  // Hook for other partial class
    }
}

// File2.cs
public partial class UserService
{
    private ICache _cache;
    
    partial void OnConstructing()
    {
        _cache = new MemoryCache();
    }
}

5. Overload Compound Assignment Operators

Custom behavior for +=, -=, *=, etc.[2]

public class Counter
{
    public int Value { get; set; }
    
    // Now you can overload +=
    public static Counter operator +=(Counter counter, int value)
    {
        counter.Value += value;
        Console.WriteLine($"Added {value}, new value: {counter.Value}");
        return counter;
    }
}

// Usage
var counter = new Counter { Value = 10 };
counter += 5;  // "Added 5, new value: 15"

6. Lambda Improvements: ref, in, out without Types

Lambdas now support parameter modifiers without explicit types.[2]

Old Way:

Func<int, int, bool> tryParse = (string input, out int result) => 
    int.TryParse(input, out result);

New Way:

// Type inference with ref/out/in
var modifier = (ref int x) => x *= 2;
var reader = (in int x) => x * 2;  // Read-only
var output = (out int x) => x = 42;

int num = 10;
modifier(ref num);  // num is now 20

7. nameof for Unbounded Generics

Get names of generic types without type parameters.[2]

// Old - required type parameter
string name1 = nameof(List<int>);  // Error in older versions

// New - works with unbounded generics
string name2 = nameof(List<>);     // "List"
string name3 = nameof(Dictionary<,>);  // "Dictionary"

// Useful for logging and reflection
void LogGenericType<T>()
{
    Console.WriteLine($"Processing {nameof(T)}");
}

8. Implicit Conversions for Span<T>

Easier span creation and usage.[2]

// Implicit conversion from arrays
Span<int> span = new int[] { 1, 2, 3, 4, 5 };

// Implicit from string
ReadOnlySpan<char> chars = "Hello";

// More natural span slicing
var slice = span[1..3];  // Even cleaner syntax

9. Switch Expression Enhancements

More powerful pattern matching in switch expressions.

// Combining patterns
string Describe(object obj) => obj switch
{
    int n when n > 0 => "Positive integer",
    int n when n < 0 => "Negative integer",
    string { Length: > 10 } => "Long string",
    string => "Short string",
    List<int> { Count: 0 } => "Empty list",
    _ => "Something else"
};

10. Collection Expressions (Enhanced)

Even more natural collection initialization.

// Spread operator in collections
int[] numbers1 = [1, 2, 3];
int[] numbers2 = [4, 5, 6];
int[] combined = [..numbers1, ..numbers2];  // [1, 2, 3, 4, 5, 6]

// Works with different collection types
List<string> list = ["a", "b", "c"];
HashSet<int> set = [1, 2, 2, 3];  // {1, 2, 3}

📋 C# 14 Features Summary

Feature Description Impact Extension Members Extend with properties, operators, static members ⭐⭐⭐⭐⭐ field keyword Simpler property backing fields ⭐⭐⭐⭐ Null-conditional ??= Cleaner null assignment ⭐⭐⭐⭐ Partial Constructors Split constructor logic ⭐⭐⭐ Compound Assignment Overloads Custom +=, -= behavior ⭐⭐⭐ Lambda ref/in/out Parameter modifiers in lambdas ⭐⭐⭐ nameof Unbounded Generic type names ⭐⭐⭐ Span Conversions Easier span usage ⭐⭐⭐⭐


🎯 Why C# 14 is a Game-Changer for 2026

C# 14 represents the most significant evolution in C# since the introduction of LINQ. Here's why these features will dominate professional C# development in 2026:

🚀 Extension Members: The #1 Feature

Why it matters:

  • Eliminates repetitive code: No more static helper classes with awkward this parameters

  • Extension Properties: Finally! Extend types with computed properties, not just methods

  • Extension Operators: Custom operators on existing types

  • Better IntelliSense: Properties and operators show up naturally in IDE suggestions

Real-world impact:

// OLD WAY (pre-C# 14) - Clunky
StringExtensions.IsEmpty(myString)
StringExtensions.WordCount(myString)

// NEW WAY (C# 14) - Natural & Intuitive
myString.IsEmpty      // Property!
myString.WordCount    // Property!

2026 Industry Adoption: Expected to be used in 80%+ of new C# codebases by mid-2026.

field Keyword: Clean Property Code

Why it matters:

  • Less boilerplate: No more manual backing fields

  • Cleaner validation: Inline property validation without extra fields

  • Better readability: Property logic stays in the property

Real-world impact:

// OLD: 5 lines of boilerplate
private string _name;
public string Name { 
    get => _name; 
    set => _name = string.IsNullOrEmpty(value) ? "Unknown" : value; 
}

// NEW: 1 clean property with field keyword
public string Name { 
    get => field; 
    set => field = string.IsNullOrEmpty(value) ? "Unknown" : value; 
}

🎓 Learning Path for C# 14 Mastery in 2026

Week 1-2: Foundation

  • Understand Extension Members concept

  • Practice converting old extension methods to extension blocks

  • Master field keyword in properties

Week 3-4: Intermediate

  • Build Extension Properties for common types (string, List<T>, DateTime)

  • Implement Extension Operators

  • Combine Extension Members with LINQ

Week 5-6: Advanced

  • Refactor real-world codebase to use C# 14

  • Build utility library with Extension Blocks

  • Master performance implications

Week 7-8: Mastery

  • Contribute to open-source with C# 14 features

  • Build production application using modern patterns

  • Teach others about C# 14


📋 C# 14 Features Summary


🎯 Practice with C# 14 Features

Exercise 1: Extension Members

Create extension blocks for:

  • string with properties like IsEmpty, WordCount

  • List<T> with properties like IsEmpty, SecondItem

  • DateTime with properties like IsWeekend, IsToday

Exercise 2: Modern Patterns

Refactor old code to use:

  • field keyword in properties

  • Extension blocks instead of extension methods

  • Null-conditional assignment

Exercise 3: Build Something New

Create a small utility library using C# 14 features:

  • Collection extension blocks

  • Custom operators

  • Modern syntax throughout


🎓 Next Steps After Mastery

Choose Your Path:

1. ASP.NET Core Web Development

  • MVC / Razor Pages

  • Web APIs

  • Blazor (WebAssembly/Server)

  • SignalR for real-time

2. Desktop Development

  • WPF (Windows Presentation Foundation)

  • WinForms

  • MAUI (Cross-platform)

3. Cloud & Microservices

  • Azure Functions

  • Docker & Kubernetes

  • Microservices architecture

  • Message queues (RabbitMQ, Azure Service Bus)

4. Game Development

  • Unity (C# scripting)

  • Game design patterns

5. Mobile Development

  • .NET MAUI

  • Xamarin


📚 Additional Resources

Official Documentation

Practice Platforms

  • LeetCode

  • HackerRank

  • Codewars

  • Exercism

YouTube Channels

  • IAmTimCorey

  • Nick Chapsas

  • Raw Coding

  • tutorialEU


✅ Completion Checklist

Level 0-1: Foundation

  • [ ] Solve 50+ basic programming problems

  • [ ] Master Git basics

  • [ ] Create GitHub profile with projects

Level 2: C# Basics

  • [ ] Complete all data types exercises

  • [ ] Build 5+ console applications

  • [ ] Master LINQ basics

Level 3: OOP

  • [ ] Implement all SOLID principles

  • [ ] Create class hierarchies

  • [ ] Use interfaces effectively

Level 4: Advanced

  • [ ] Write async/await code

  • [ ] Master LINQ complex queries

  • [ ] Handle exceptions properly

Level 5: Data

  • [ ] Build CRUD with ADO.NET

  • [ ] Master EF Core

  • [ ] Design normalized databases

Level 6: Professional

  • [ ] Write unit tests (>80% coverage)

  • [ ] Implement 5+ design patterns

  • [ ] Build 3 portfolio projects

Level 7: Master C# 14 (2026 Focus)

  • [ ] Master C# 14 Extension Members (Extension Blocks)

  • [ ] Apply field keyword in all properties

  • [ ] Implement Extension Properties & Operators

  • [ ] Use modern collection expressions

  • [ ] Refactor legacy code to C# 14 patterns

  • [ ] Build production app using C# 14 features


🎊 Congratulations!

Once you complete this roadmap, you'll be a proficient C# developer ready for:

  • Junior/Mid-level developer positions

  • Contributing to open-source projects

  • Building production applications

  • Specializing in your chosen path

Keep learning, keep coding, and never stop improving! 🚀

Tags
c#csharp.netcsharp roadmaproadmap