6 questions foundEncapsulation (hide state behind members), Inheritance (derive types), Polymorphism (one interface, many implementations) and Abstraction (expose essentials, hide detail).
public abstract class Shape { public abstract double Area(); }
public class Square(double s) : Shape { public override double Area() => s*s; }
Real-world example A renderer iterates List<Shape> and calls Area() without knowing concrete types.
Properties expose get/set accessors over a private field, letting you validate on set and compute on get while keeping the field hidden.
public int Age { get; private set; }
public decimal Discount { get => _d; set => _d = Math.Clamp(value,0,100); }
Real-world example Clamping a discount to 0-100 so no caller can set an invalid value.
override replaces a virtual/abstract base member and uses run-time (polymorphic) dispatch; new hides the base member and dispatch depends on the compile-time type, which is usually a bug.
class B { public virtual void Go()=>Console.Write("B"); }
class D : B { public override void Go()=>Console.Write("D"); }
Real-world example Overriding lets a PdfReport customise Render() while base code calls it polymorphically.
sealed stops a class being inherited or a member being further overridden. It can protect invariants and lets the JIT devirtualise calls for speed.
public sealed class Logger { }
Real-world example Sealing a security-sensitive type so no subclass can weaken its behaviour.
Composition builds behaviour by holding other objects rather than inheriting; it avoids deep, rigid hierarchies and the fragile base-class problem. Prefer it unless there is a true is-a relationship.
class Car { private readonly Engine _engine; public Car(Engine e)=>_engine=e; }
Real-world example Injecting an IPaymentGateway into an OrderService instead of subclassing a base service.
Each type with virtual members has a method table; a virtual call looks up the actual implementation at run time via the object's type. It costs an indirection versus a direct/sealed call.
Shape s = new Square(3);
double a = s.Area(); // resolved to Square.Area at run time
Real-world example Hot loops sometimes seal types or use generics to let the JIT remove virtual dispatch.