C# & .NET Framework Advanced OOP — Mastering the Craft
💡
Exercise 38

Abstract Classes — The Master Blueprint 20 XP Hard

Ctrl+Enter Run Ctrl+S Save

📐 Abstract Classes: The Blueprint You Can't Build Directly

An abstract class is a class that cannot be instantiated on its own. It serves as an incomplete blueprint — it defines shared behavior and forces derived classes to fill in the blanks. You get the structure for free; the specifics are your responsibility.

Imagine the city's chief architect draws a master blueprint for all buildings. It specifies that every building must have a permit process and a description, but the exact permit type and description depend on whether it's commercial or residential. You can't construct "a blueprint" — you construct a specific building that follows the blueprint.

The abstract Keyword

Mark a class as abstract to prevent direct instantiation. Mark methods as abstract to force derived classes to provide their own implementation. Abstract methods have no body — just a signature with a semicolon.

abstract class Shape { public string Color; // Abstract method — derived classes MUST implement public abstract double Area(); // Regular method — inherited as-is public void Describe() { Console.WriteLine($"A {Color} shape with area {Area()}"); } } class Circle : Shape { public double Radius; public Circle(double radius, string color) { Radius = radius; Color = color; } // MUST override the abstract method public override double Area() => Math.PI * Radius * Radius; } // Shape s = new Shape(); // ERROR! Cannot instantiate abstract class Circle c = new Circle(5, "Red"); c.Describe(); // "A Red shape with area 78.539..."

Abstract vs Virtual

Abstract methods have no body and must be overridden. Virtual methods have a default body and can be overridden. Use abstract when there's no sensible default; use virtual when there is.

abstract class Animal { public abstract void Speak(); // No default — MUST override public virtual void Sleep() // Has default — CAN override { Console.WriteLine("Zzz..."); } } class Cat : Animal { public override void Speak() => Console.WriteLine("Meow!"); // Sleep() is inherited as-is — prints "Zzz..." }

The sealed Keyword — Locking the Chain

The sealed keyword prevents a class from being inherited further, or prevents a specific method from being overridden in deeper derived classes. It's the architect saying: "This design is final. No modifications."

sealed class FinalBuilding : Building { // No class can inherit from FinalBuilding } class MiddleClass : Shape { public sealed override double Area() => 0; // No further override allowed }

Template Method Pattern

Abstract classes are perfect for the Template Method design pattern: define the skeleton of an algorithm in the base class, and let derived classes fill in specific steps.

abstract class BuildingBlueprint { // Template method — defines the sequence public void Construct() { LayFoundation(); BuildStructure(); // abstract — each building decides GetPermits(); // abstract — each building decides Console.WriteLine("Construction complete!"); } private void LayFoundation() { Console.WriteLine("Laying foundation..."); } protected abstract void BuildStructure(); protected abstract void GetPermits(); }
  • abstract class — cannot be instantiated; may contain abstract and concrete members
  • abstract method — no body; derived classes must override it
  • virtual method — has a body; derived classes may optionally override it
  • sealed class — cannot be inherited from
  • sealed override — prevents further overriding in deeper subclasses
  • When to use abstract vs interface: Use abstract when you need shared state/code; use interface for pure contracts across unrelated types
📋 Instructions
**Your Mission: Enforce the Master Blueprint!** Create an abstract `BuildingBlueprint` and two concrete classes that follow the blueprint. 1. Create an `abstract class BuildingBlueprint`: - A `protected string Name` field - A constructor that takes a `string name` and stores it - An abstract method `protected abstract string GetPermitZone()` - A concrete method `public void Describe()` that prints `"{Type}: {Name} (permits: {zone})"` where `{Type}` comes from an abstract property/method and `{zone}` comes from `GetPermitZone()` - An abstract method `protected abstract string GetType()` 2. Create a `CommercialBuilding` class extending `BuildingBlueprint`: - Constructor takes a name and calls `base(name)` - `GetType()` returns `"Commercial"` - `GetPermitZone()` returns `"commercial zone"` 3. Create a `ResidentialBuilding` class extending `BuildingBlueprint`: - Constructor takes a name and calls `base(name)` - `GetType()` returns `"Residential"` - `GetPermitZone()` returns `"residential zone"` 4. In `Main`: - Create a `CommercialBuilding("Office Hub")` and call `Describe()` - Create a `ResidentialBuilding("Park View")` and call `Describe()` - Print `"Blueprint enforced for all buildings!"`
The abstract class has two abstract methods — `GetType()` and `GetPermitZone()`. The `Describe()` method in the base class calls both: `Console.WriteLine($"{GetType()}: {Name} (permits: {GetPermitZone()})");`. Note: since `System.Object` already has a `GetType()` method, you can use the `new` keyword: `protected new abstract string GetType();` — or simply name it `GetBuildingType()` to avoid the conflict.
main.py
Hi! I'm Rex 👋
Output
Ready. Press ▶ Run or Ctrl+Enter.