After this lesson, you will be able to: Write C# classes with interfaces, abstract classes, inheritance, polymorphism, access modifiers, and auto-properties.
C# is OOP through and through. Get the patterns right and you'll read .NET source code (which is open) like a book.
Auto-properties cut boilerplate.
public class User {// Auto-properties (C# 3+), compiler generates the backing fieldpublic string Name { get; set; } // mutablepublic string Email { get; init; } // init-only (C# 9+), set once in init or constructorpublic int Age { get; private set; } // mutable internally; readonly from outsidepublic DateTime CreatedAt { get; } = DateTime.UtcNow; // readonly with default// Primary constructor (C# 12+)public User(string name, string email) {Name = name;Email = email;}public void Birthday() => Age++;// Override ToString for nice debug outputpublic override string ToString() => $"{Name} <{Email}>";}alex.Birthday();
Interfaces define contracts; abstract classes share partial impl.
// Interface (prefer for contracts)public interface IRepository<T> {Task<T?> FindByIdAsync(int id);Task SaveAsync(T item);}// Implementationpublic class UserRepository : IRepository<User> {public Task<User?> FindByIdAsync(int id) {// ... DB lookupreturn Task.FromResult<User?>(null);}public Task SaveAsync(User user) {// ... savereturn Task.CompletedTask;}}// Abstract class, partial implementationpublic abstract class Shape {public abstract double Area(); // must overridepublic virtual string Describe() => $"Shape with area {Area()}"; // can override}public class Circle : Shape {public double Radius { get; init; }public override double Area() => Math.PI * Radius * Radius;}
Use when modeling data with equality by value.
// Record: auto ToString, auto Equals (by value), immutable by defaultpublic record User(string Name, string Email, int Age);Console.WriteLine(a == b); // True (record equality by value!)// Records support 'with' expressions for non-destructive mutationvar older = a with { Age = 31 }; // new record with Age changed// Record struct (value type record, C# 10+)public record struct Point(double X, double Y);
ALWAYS expose properties, not fields. Properties can be virtual, can have logic, can be readonly externally. Private fields backing properties are fine. Public fields are a code smell, refactoring later changes the binary interface. Auto-properties (get; set;) make this almost free.
Public fields instead of properties (breaks ABI when refactoring). Deep inheritance hierarchies (favor interfaces + composition). Forgetting `override` (the new method shadows instead of overrides, bugs). Mutable records (defeats the value-equality purpose).
Sign in and purchase access to unlock this lesson.