C# Programming

Complete Guide: OOP, LINQ, Async & .NET Fundamentals

C# Basics

C#
using System;

namespace MyApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // Output
            Console.WriteLine("Hello, World!");
            
            // Variables
            int age = 25;
            double salary = 50000.50;
            string name = "Alice";
            bool isActive = true;
            char grade = 'A';
            
            // var - Implicit typing
            var count = 10;        // int
            var price = 99.99;     // double
            
            // Constants
            const int MAX_SIZE = 100;
            
            // Nullable types
            int? nullableInt = null;
            nullableInt = 5;
            
            // Null coalescing
            int value = nullableInt ?? 0;
            
            // Input
            Console.Write("Enter your name: ");
            string input = Console.ReadLine();
            
            // String interpolation
            Console.WriteLine($"Hello, {name}! Age: {age}");
            
            // Type conversion
            int num = int.Parse("42");
            int.TryParse("123", out int result);
            string str = age.ToString();
        }
    }
}

Control Flow

C#
// if-else
if (age >= 18)
    Console.WriteLine("Adult");
else if (age >= 13)
    Console.WriteLine("Teen");
else
    Console.WriteLine("Child");

// Switch (traditional)
switch (grade)
{
    case 'A':
        Console.WriteLine("Excellent!");
        break;
    case 'B':
        Console.WriteLine("Good!");
        break;
    default:
        Console.WriteLine("Try harder!");
        break;
}

// Switch expression (C# 8+)
string result = grade switch
{
    'A' => "Excellent!",
    'B' => "Good!",
    _ => "Try harder!"
};

// for loop
for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}

// foreach
string[] names = { "Alice", "Bob", "Charlie" };
foreach (string n in names)
{
    Console.WriteLine(n);
}

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

Object-Oriented Programming

C#
// Class definition
public class Person
{
    // Fields (private by default)
    private string _name;
    private int _age;
    
    // Auto-implemented property
    public string Email { get; set; }
    
    // Property with backing field
    public string Name
    {
        get => _name;
        set => _name = value ?? "Unknown";
    }
    
    // Read-only property
    public int Age
    {
        get => _age;
        private set => _age = value >= 0 ? value : 0;
    }
    
    // Constructor
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
    
    // Method
    public void Introduce()
    {
        Console.WriteLine($"Hi, I'm {Name}, {Age} years old.");
    }
    
    // Static member
    public static int Count { get; private set; } = 0;
}

// Inheritance
public class Employee : Person
{
    public decimal Salary { get; set; }
    
    public Employee(string name, int age, decimal salary)
        : base(name, age)
    {
        Salary = salary;
    }
    
    // Override
    public override string ToString()
    {
        return $"{Name} - ${Salary}";
    }
}

// Interface
public interface IWorkable
{
    void Work();
    string JobTitle { get; set; }
}

// Abstract class
public abstract class Shape
{
    public abstract double Area();
    public virtual void Draw() => Console.WriteLine("Drawing shape");
}

public class Circle : Shape
{
    public double Radius { get; set; }
    
    public override double Area() => Math.PI * Radius * Radius;
    
    public override void Draw() => Console.WriteLine("Drawing circle");
}

Collections

C#
using System.Collections.Generic;

// Arrays
int[] numbers = { 1, 2, 3, 4, 5 };
int[] nums = new int[5];
string[,] matrix = new string[3, 3];

// List - Dynamic array
List<int> list = new List<int> { 1, 2, 3 };
list.Add(4);
list.Remove(2);
list.Insert(0, 0);
list.RemoveAt(0);
bool exists = list.Contains(3);
int count = list.Count;

// Dictionary - Key-value pairs
Dictionary<string, int> ages = new Dictionary<string, int>
{
    { "Alice", 25 },
    { "Bob", 30 }
};
ages["Charlie"] = 35;
ages.TryGetValue("Alice", out int age);

foreach (var kvp in ages)
{
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}

// HashSet - Unique elements
HashSet<int> set = new HashSet<int> { 1, 2, 3, 3 }; // {1, 2, 3}
set.Add(4);
set.Remove(1);
bool contains = set.Contains(2);

// Queue - FIFO
Queue<string> queue = new Queue<string>();
queue.Enqueue("First");
queue.Enqueue("Second");
string first = queue.Dequeue();  // "First"
string peek = queue.Peek();      // "Second"

// Stack - LIFO
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
int top = stack.Pop();    // 2
int look = stack.Peek();  // 1

LINQ (Language Integrated Query)

C#
using System.Linq;

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

// Where - Filter
var evens = numbers.Where(n => n % 2 == 0);

// Select - Transform
var doubled = numbers.Select(n => n * 2);

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

// First / Last / Single
int first = numbers.First();
int last = numbers.Last();
int firstEven = numbers.First(n => n % 2 == 0);
int firstOrDefault = numbers.FirstOrDefault(n => n > 100);  // 0

// Any / All
bool hasEvens = numbers.Any(n => n % 2 == 0);  // true
bool allPositive = numbers.All(n => n > 0);    // true

// Count / Sum / Average / Max / Min
int count = numbers.Count();
int sum = numbers.Sum();
double avg = numbers.Average();
int max = numbers.Max();

// Take / Skip
var firstThree = numbers.Take(3);       // 1, 2, 3
var skipThree = numbers.Skip(3);        // 4, 5, 6, ...

// Distinct
var unique = numbers.Distinct();

// GroupBy
var grouped = numbers.GroupBy(n => n % 2 == 0 ? "Even" : "Odd");

// Query syntax (alternative)
var query = from n in numbers
            where n > 5
            orderby n descending
            select n * 2;

// ToList / ToArray / ToDictionary
List<int> list = numbers.Where(n => n > 5).ToList();
int[] array = numbers.ToArray();
Dictionary<int, int> dict = numbers.ToDictionary(n => n, n => n * n);

Async/Await

C#
using System.Threading.Tasks;
using System.Net.Http;

// Async method
public async Task<string> GetDataAsync(string url)
{
    using HttpClient client = new HttpClient();
    string result = await client.GetStringAsync(url);
    return result;
}

// Async void (only for event handlers)
public async void ButtonClick(object sender, EventArgs e)
{
    await DoSomethingAsync();
}

// Task.Run for CPU-bound work
public async Task ProcessDataAsync()
{
    int result = await Task.Run(() =>
    {
        // Heavy computation
        return ExpensiveCalculation();
    });
}

// Multiple tasks
public async Task ProcessMultipleAsync()
{
    Task<string> task1 = GetDataAsync("url1");
    Task<string> task2 = GetDataAsync("url2");
    
    // Wait for all
    await Task.WhenAll(task1, task2);
    
    // Or wait for any
    Task<string> first = await Task.WhenAny(task1, task2);
}

// Cancellation
public async Task CancellableMethodAsync(CancellationToken token)
{
    for (int i = 0; i < 100; i++)
    {
        token.ThrowIfCancellationRequested();
        await Task.Delay(100, token);
    }
}

// Usage
var cts = new CancellationTokenSource();
cts.CancelAfter(5000);  // Cancel after 5 seconds

try
{
    await CancellableMethodAsync(cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation was cancelled");
}

Delegates & Events

C#
// Delegate declaration
public delegate int MathOperation(int a, int b);

// Using delegate
MathOperation add = (a, b) => a + b;
MathOperation multiply = (a, b) => a * b;
int result = add(3, 5);  // 8

// Built-in delegates
Func<int, int, int> subtract = (a, b) => a - b;      // Returns value
Action<string> print = msg => Console.WriteLine(msg); // No return
Predicate<int> isEven = n => n % 2 == 0;             // Returns bool

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

// Custom event args
public class DataEventArgs : EventArgs
{
    public string Data { get; set; }
}

public class DataProcessor
{
    public event EventHandler<DataEventArgs> DataProcessed;
    
    protected virtual void OnDataProcessed(string data)
    {
        DataProcessed?.Invoke(this, new DataEventArgs { Data = data });
    }
    
    public void Process()
    {
        // Processing...
        OnDataProcessed("Processed data");
    }
}

// Subscribe to events
var processor = new DataProcessor();
processor.DataProcessed += (sender, e) =>
{
    Console.WriteLine($"Data: {e.Data}");
};

Modern C# Features

C#
// Records (C# 9+) - Immutable data types
public record Person(string Name, int Age);
var p1 = new Person("Alice", 25);
var p2 = p1 with { Age = 26 };  // Non-destructive mutation

// Init-only properties
public class Config
{
    public string Setting { get; init; }
}

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

// Switch expression with patterns
string GetDescription(object obj) => obj switch
{
    int i when i > 0 => "Positive",
    int i when i < 0 => "Negative",
    int => "Zero",
    string s => $"String: {s}",
    null => "Null",
    _ => "Unknown"
};

// Null-conditional operators
string name = person?.Name;
int? length = person?.Name?.Length;

// Null-coalescing assignment (C# 8+)
list ??= new List<int>();

// Target-typed new (C# 9+)
List<int> numbers = new();
Dictionary<string, int> dict = new();

// Top-level statements (C# 9+)
// Program.cs can be just:
Console.WriteLine("Hello, World!");

// Global usings (C# 10+)
// global using System.Collections.Generic;

// File-scoped namespace (C# 10+)
// namespace MyApp;

// Raw string literals (C# 11+)
var json = """
    {
        "name": "Alice",
        "age": 25
    }
    """;
C# Best Practices:
• Use var when type is obvious
• Prefer async/await for I/O operations
• Use records for DTOs
• Handle nulls with ?. and ??
• Use LINQ for collections