C#: OOP & Type System
Classes & Inheritance
// Class with properties and constructor
public class Animal
{
public string Name { get; init; } // init-only — set only in constructor/initializer
public int Age { get; private set; } // public read, private write
protected string Species { get; set; }
private static int _count;
public static int Count => _count;
public Animal(string name, int age, string species)
{
Name = name;
Age = age;
Species = species;
_count++;
}
// Virtual — can be overridden
public virtual string Describe() => $"{Species} named {Name}";
// Sealed — prevent inheritance of this method
public sealed override string ToString() => Describe();
// Static factory
public static Animal Create(string name) => new("Unknown", 0, name);
}
// Inheritance
public class Dog : Animal
{
public string Breed { get; }
public Dog(string name, int age, string breed)
: base(name, age, "Dog") // call base constructor
{
Breed = breed;
}
public override string Describe() => $"{base.Describe()} ({Breed})";
public void Fetch() => Console.WriteLine($"{Name} fetches!");
}
// Object initializer syntax (uses init/set properties)
var dog = new Dog("Rex", 3, "Labrador") { };Interfaces & Abstract Classes
// Interface — defines contract
public interface IRepository<T, TKey>
{
Task<T?> GetByIdAsync(TKey id);
Task<IEnumerable<T>> GetAllAsync();
Task<T> CreateAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(TKey id);
}
// Default interface methods (C# 8+)
public interface ILogger
{
void Log(string message);
void LogError(string message) => Log($"ERROR: {message}"); // default implementation
}
// Abstract class — partial implementation
public abstract class Shape
{
public abstract double Area(); // must be overridden
public abstract double Perimeter();
public void Print() => // concrete — shared implementation
Console.WriteLine($"Area: {Area()}, Perimeter: {Perimeter()}");
}
public class Circle : Shape
{
public double Radius { get; }
public Circle(double radius) => Radius = radius;
public override double Area() => Math.PI * Radius * Radius;
public override double Perimeter() => 2 * Math.PI * Radius;
}
// Explicit interface implementation (resolve name conflicts)
public class MultiLogger : ILogger, IDisposable
{
void ILogger.Log(string message) => Console.WriteLine(message);
public void Dispose() => Console.WriteLine("Disposed");
}Generics
// Generic class
public class Stack<T>
{
private readonly List<T> _items = new();
public void Push(T item) => _items.Add(item);
public T Pop()
{
var item = _items[^1];
_items.RemoveAt(_items.Count - 1);
return item;
}
public T Peek() => _items[^1];
public int Count => _items.Count;
}
// Generic method with constraint
public T Max<T>(T a, T b) where T : IComparable<T>
=> a.CompareTo(b) >= 0 ? a : b;
// Multiple constraints
public class Repository<T> where T : class, IEntity, new()
{
// T must be a class, implement IEntity, and have parameterless constructor
}
// Covariance (out) and contravariance (in)
IEnumerable<Dog> dogs = GetDogs();
IEnumerable<Animal> animals = dogs; // IEnumerable<out T> is covariant — works!
Action<Animal> actOnAnimal = a => Console.WriteLine(a.Name);
Action<Dog> actOnDog = actOnAnimal; // Action<in T> is contravariant — works!Delegates, Events & Lambdas
// Delegate — type-safe function pointer
public delegate int Operation(int a, int b);
Operation add = (a, b) => a + b;
int result = add(3, 4); // 7
// Built-in delegate types (prefer over custom delegates)
Func<int, int, int> multiply = (a, b) => a * b; // returns value
Action<string> print = s => Console.WriteLine(s); // returns void
Predicate<int> isEven = n => n % 2 == 0; // returns bool
// Events (publisher-subscriber pattern)
public class Button
{
public event EventHandler<ClickEventArgs>? Clicked;
protected virtual void OnClicked(ClickEventArgs e) =>
Clicked?.Invoke(this, e); // ?. — thread-safe null check
public void Click() => OnClicked(new ClickEventArgs { X = 10, Y = 20 });
}
// Subscribe
var btn = new Button();
btn.Clicked += (sender, e) => Console.WriteLine($"Clicked at {e.X},{e.Y}");
btn.Click();
// Lambda expressions
var numbers = new[] { 1, 2, 3, 4, 5 };
var even = numbers.Where(n => n % 2 == 0); // lambda
var squares = numbers.Select(n => n * n); // projection
var sum = numbers.Aggregate(0, (acc, n) => acc + n); // foldKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free